<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2019, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

import _ from 'lodash';
import sundial from 'sundial';

import TZOUtil from '../../TimezoneOffsetUtil';
import annotate from '../../eventAnnotations';
import { remote } from 'electron';

const isBrowser = typeof window !== 'undefined';
// eslint-disable-next-line no-console
const debug = isBrowser ? require('bows')('BluetoothLEDriver') : console.log;

const KETONE_VALUE_FACTOR = 10;
const KETONE_HI = 8.0;

module.exports = (config) => {
  const cfg = _.clone(config);
  cfg.deviceTags = ['bgm'];

  // With no date & time settings changes available,
  // timezone is applied across-the-board
  cfg.tzoUtil = new TZOUtil(cfg.timezone, new Date().toISOString(), []);

  return {
   /* eslint no-param-reassign:
      [ "error", { "props": true, "ignorePropertyModificationsFor": ["data"] } ] */

   detect(deviceInfo, cb) {
     debug('no detect function needed', deviceInfo);
     cb(null, deviceInfo);
   },

   setup(deviceInfo, progress, cb) {
     debug('in setup!');
     progress(100);
     cb(null, { deviceInfo });
   },

   connect(progress, data, cb) {
     debug('in connect!');

     (async () => {
       await cfg.deviceComms.ble.connectTimeout();
     })().then(() => {
       return cb(null, data);
     }).catch((error) => {
       debug('Error in connect: ', error);
       return cb(error, null);
     });
   },

   getConfigInfo(progress, data, cb) {
     debug('in getConfigInfo', data);
     progress(0);

     (async () => {
       _.assign(cfg.deviceInfo , await cfg.deviceComms.ble.getDeviceInfo());
     })().then(() => {
       if (!cfg.deviceInfo.name.startsWith('CareSens') && !cfg.deviceInfo.name.startsWith('ReliOn 2395')) {
         return cb (new Error('We don\'t currently support this meter.'));
       }

       cfg.deviceTags = ['bgm'];
       cfg.deviceInfo.deviceId = `${[cfg.deviceInfo.manufacturers]}-${cfg.deviceInfo.model}-${remote.getGlobal('bluetoothDeviceId')}`;
       data.deviceModel = cfg.deviceInfo.model; // for metrics
       cfg.builder.setDefaults({ deviceId: cfg.deviceInfo.deviceId });
       cfg.reading = false;
       return cb(null, data);
     }).catch((error) => {
       debug('Error in getConfigInfo: ', error);
       return cb(error, null);
     });
   },

   fetchData(progress, data, cb) {
     debug('in fetchData', data);

     const abortTimer = setTimeout(() => {
       debug('TIMEOUT');
       return cb('Timeout error. Did the meter pair succesfully?', null);
     }, 30000); // give enough time to confirm pairing on meter

     cfg.deviceComms.ble.once('data', (result) => {
       debug('Records:', result);
       _.assign(data, result);
       return cb(null, data);
     });

     cfg.deviceComms.ble.once('numberOfRecords', async (result) => {
       debug('Number of records:', result);
       clearTimeout(abortTimer);
       if (!cfg.reading) {
         cfg.reading = true;
         setTimeout(async () => {
           debug('Getting all records..');
           await cfg.deviceComms.ble.getAllRecords();
         }, 500); // wait 500ms to prevent "GATT operation already in progress" error
       }
     });

     (async () => {
       await cfg.deviceComms.ble.getNumberOfRecords();
     })().catch((error) => {
       debug('Error:', error);
       return cb(error, null);
     });
   },

   processData(progress, data, cb) {
     progress(0);
     data.post_records = [];

     _.forEach(data.records, (result) => {
       const record = result;
       let annotation = null;
       let isKetone = false;

       if (record.hasContext) {
         const context = _.find(
           data.contextRecords,
           { 'seqNum' : record.seqNum }
         );

         if (context) {
           if (context.meal && context.meal === 6) {
             isKetone = true;
           }
         } else {
           throw new Error ('Could not find context of measurement');
         }
       }

       if (isKetone) {
         // According to spec, HI > 8 mmol/L
         // there is no LO as values are between 0 and 8 mmol/L
         if (record.value > (KETONE_HI * KETONE_VALUE_FACTOR) ) {
           record.value = KETONE_HI + (1 / KETONE_VALUE_FACTOR);
           annotation = {
             code: 'ketone/out-of-range',
             threshold: KETONE_HI,
             value: 'high',
           };
         } else {
           record.value /= KETONE_VALUE_FACTOR;
         }
       } else {
         if (record.value > 600) {
           record.value = 601;
           annotation = {
             code: 'bg/out-of-range',
             threshold: 600,
             value: 'high',
           };
         } else if (record.value < 20) {
           record.value = 19;
           annotation = {
             code: 'bg/out-of-range',
             threshold: 20,
             value: 'low',
           };
         }
       }

       if (record.type !== 10) { //check that it's not control solution
         let postRecord = null;

         if (isKetone) {
           postRecord = cfg.builder.makeBloodKetone()
             .with_value(record.value)
             .with_units('mmol/L'); // ketones are hard-coded in mmol/L
         } else {
           postRecord = cfg.builder.makeSMBG()
             .with_value(record.value)
             .with_units(record.units);
         }

         postRecord
            .with_deviceTime(sundial.formatDeviceTime(record.timestamp))
            .set('index', record.seqNum);

         if (annotation) {
           annotate.annotateEvent(postRecord, annotation);
         }

         cfg.tzoUtil.fillInUTCInfo(postRecord, record.timestamp);
         delete postRecord.index;

         data.post_records.push(postRecord.done());
       }
     });

     debug('POST records:', data.post_records);

     if (data.post_records.length === 0) {
       debug('Device has no records to upload');
       return cb(new Error('Device has no records to upload'), null);
     }

     progress(100);
     return cb(null, data);
   },

   uploadData(progress, data, cb) {
     progress(0);

     const sessionInfo = {
       deviceTags: cfg.deviceTags,
       deviceManufacturers: cfg.deviceInfo.manufacturers,
       deviceModel: cfg.deviceInfo.model,
       deviceId: cfg.deviceInfo.deviceId,
       deviceSerialNumber: cfg.deviceInfo.serial,
       start: sundial.utcDateString(),
       timeProcessing: cfg.tzoUtil.type,
       tzName: cfg.timezone,
       version: cfg.version,
     };

     cfg.api.upload.toPlatform(
       data.post_records, sessionInfo, progress, cfg.groupId,
       (err, result) => {
         progress(100);

         if (err) {
           debug(err);
           debug(result);
           return cb(err, data);
         }
         data.cleanup = true;
         return cb(null, data);
       },
       'dataservices',
     );
   },

   disconnect(progress, data, cb) {
     // performing disconnect in cleanup
     data.disconnect = true;
     cb(null, data);
   },

   cleanup(progress, data, cb) {
     debug('in cleanup');

     (async () => {
       cfg.reading = false;
       await cfg.deviceComms.ble.disconnect();
     })().then(() => {
       progress(100);
       data.cleanup = true;
       return cb();
     }).catch((error) => {
       debug('Error during disconnect: ', error);
       return cb();
    });
   },
  };
};
<commit_msg>remove all listeners<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2019, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

import _ from 'lodash';
import sundial from 'sundial';

import TZOUtil from '../../TimezoneOffsetUtil';
import annotate from '../../eventAnnotations';
import { remote } from 'electron';

const isBrowser = typeof window !== 'undefined';
// eslint-disable-next-line no-console
const debug = isBrowser ? require('bows')('BluetoothLEDriver') : console.log;

const KETONE_VALUE_FACTOR = 10;
const KETONE_HI = 8.0;

module.exports = (config) => {
  const cfg = _.clone(config);
  cfg.deviceTags = ['bgm'];

  // With no date & time settings changes available,
  // timezone is applied across-the-board
  cfg.tzoUtil = new TZOUtil(cfg.timezone, new Date().toISOString(), []);

  return {
   /* eslint no-param-reassign:
      [ "error", { "props": true, "ignorePropertyModificationsFor": ["data"] } ] */

   detect(deviceInfo, cb) {
     debug('no detect function needed', deviceInfo);
     cb(null, deviceInfo);
   },

   setup(deviceInfo, progress, cb) {
     debug('in setup!');
     progress(100);
     cb(null, { deviceInfo });
   },

   connect(progress, data, cb) {
     debug('in connect!');

     (async () => {
       await cfg.deviceComms.ble.connectTimeout();
     })().then(() => {
       return cb(null, data);
     }).catch((error) => {
       debug('Error in connect: ', error);
       return cb(error, null);
     });
   },

   getConfigInfo(progress, data, cb) {
     debug('in getConfigInfo', data);
     progress(0);

     (async () => {
       _.assign(cfg.deviceInfo , await cfg.deviceComms.ble.getDeviceInfo());
     })().then(() => {
       if (!cfg.deviceInfo.name.startsWith('CareSens') && !cfg.deviceInfo.name.startsWith('ReliOn 2395')) {
         return cb (new Error('We don\'t currently support this meter.'));
       }

       cfg.deviceTags = ['bgm'];
       cfg.deviceInfo.deviceId = `${[cfg.deviceInfo.manufacturers]}-${cfg.deviceInfo.model}-${remote.getGlobal('bluetoothDeviceId')}`;
       data.deviceModel = cfg.deviceInfo.model; // for metrics
       cfg.builder.setDefaults({ deviceId: cfg.deviceInfo.deviceId });
       cfg.reading = false;
       return cb(null, data);
     }).catch((error) => {
       debug('Error in getConfigInfo: ', error);
       return cb(error, null);
     });
   },

   fetchData(progress, data, cb) {
     debug('in fetchData', data);

     const abortTimer = setTimeout(() => {
       debug('TIMEOUT');
       return cb('Timeout error. Did the meter pair succesfully?', null);
     }, 30000); // give enough time to confirm pairing on meter

     cfg.deviceComms.ble.once('data', (result) => {
       debug('Records:', result);
       _.assign(data, result);
       return cb(null, data);
     });

     cfg.deviceComms.ble.once('numberOfRecords', async (result) => {
       debug('Number of records:', result);
       clearTimeout(abortTimer);
       if (!cfg.reading) {
         cfg.reading = true;
         setTimeout(async () => {
           debug('Getting all records..');
           await cfg.deviceComms.ble.getAllRecords();
         }, 500); // wait 500ms to prevent "GATT operation already in progress" error
       }
     });

     (async () => {
       await cfg.deviceComms.ble.getNumberOfRecords();
     })().catch((error) => {
       debug('Error:', error);
       return cb(error, null);
     });
   },

   processData(progress, data, cb) {
     progress(0);
     data.post_records = [];

     _.forEach(data.records, (result) => {
       const record = result;
       let annotation = null;
       let isKetone = false;

       if (record.hasContext) {
         const context = _.find(
           data.contextRecords,
           { 'seqNum' : record.seqNum }
         );

         if (context) {
           if (context.meal && context.meal === 6) {
             isKetone = true;
           }
         } else {
           throw new Error ('Could not find context of measurement');
         }
       }

       if (isKetone) {
         // According to spec, HI > 8 mmol/L
         // there is no LO as values are between 0 and 8 mmol/L
         if (record.value > (KETONE_HI * KETONE_VALUE_FACTOR) ) {
           record.value = KETONE_HI + (1 / KETONE_VALUE_FACTOR);
           annotation = {
             code: 'ketone/out-of-range',
             threshold: KETONE_HI,
             value: 'high',
           };
         } else {
           record.value /= KETONE_VALUE_FACTOR;
         }
       } else {
         if (record.value > 600) {
           record.value = 601;
           annotation = {
             code: 'bg/out-of-range',
             threshold: 600,
             value: 'high',
           };
         } else if (record.value < 20) {
           record.value = 19;
           annotation = {
             code: 'bg/out-of-range',
             threshold: 20,
             value: 'low',
           };
         }
       }

       if (record.type !== 10) { //check that it's not control solution
         let postRecord = null;

         if (isKetone) {
           postRecord = cfg.builder.makeBloodKetone()
             .with_value(record.value)
             .with_units('mmol/L'); // ketones are hard-coded in mmol/L
         } else {
           postRecord = cfg.builder.makeSMBG()
             .with_value(record.value)
             .with_units(record.units);
         }

         postRecord
            .with_deviceTime(sundial.formatDeviceTime(record.timestamp))
            .set('index', record.seqNum);

         if (annotation) {
           annotate.annotateEvent(postRecord, annotation);
         }

         cfg.tzoUtil.fillInUTCInfo(postRecord, record.timestamp);
         delete postRecord.index;

         data.post_records.push(postRecord.done());
       }
     });

     debug('POST records:', data.post_records);

     if (data.post_records.length === 0) {
       debug('Device has no records to upload');
       return cb(new Error('Device has no records to upload'), null);
     }

     progress(100);
     return cb(null, data);
   },

   uploadData(progress, data, cb) {
     progress(0);

     const sessionInfo = {
       deviceTags: cfg.deviceTags,
       deviceManufacturers: cfg.deviceInfo.manufacturers,
       deviceModel: cfg.deviceInfo.model,
       deviceId: cfg.deviceInfo.deviceId,
       deviceSerialNumber: cfg.deviceInfo.serial,
       start: sundial.utcDateString(),
       timeProcessing: cfg.tzoUtil.type,
       tzName: cfg.timezone,
       version: cfg.version,
     };

     cfg.api.upload.toPlatform(
       data.post_records, sessionInfo, progress, cfg.groupId,
       (err, result) => {
         progress(100);

         if (err) {
           debug(err);
           debug(result);
           return cb(err, data);
         }
         data.cleanup = true;
         return cb(null, data);
       },
       'dataservices',
     );
   },

   disconnect(progress, data, cb) {
     // performing disconnect in cleanup
     data.disconnect = true;
     cb(null, data);
   },

   cleanup(progress, data, cb) {
     debug('in cleanup');

     (async () => {
       cfg.reading = false;
       cfg.deviceComms.ble.removeAllListeners('data');
       cfg.deviceComms.ble.removeAllListeners('numberOfRecords');
       await cfg.deviceComms.ble.disconnect();
     })().then(() => {
       progress(100);
       data.cleanup = true;
       return cb();
     }).catch((error) => {
       debug('Error during disconnect: ', error);
       return cb();
    });
   },
  };
};
<|endoftext|>


<commit_before>var Utils = require('./utilities')
  , _browser = 'unknown'
  ;

// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
if (/*@cc_on!@*/false || !!document.documentMode) { // internet explorer
  _browser = 'ie';
}

module.exports = {
  svgNS:  'http://www.w3.org/2000/svg'
, xmlNS:  'http://www.w3.org/XML/1998/namespace'
, xmlnsNS:  'http://www.w3.org/2000/xmlns/'
, xlinkNS:  'http://www.w3.org/1999/xlink'
, evNS:  'http://www.w3.org/2001/xml-events'

  /**
   * Get svg dimensions: width and height
   *
   * @param  {SVGSVGElement} svg
   * @return {Object}     {width: 0, height: 0}
   */
, getBoundingClientRectNormalized: function(svg) {
    if (svg.clientWidth && svg.clientHeight) {
      return {width: svg.clientWidth, height: svg.clientHeight}
    } else if (!!svg.getBoundingClientRect()) {
      return svg.getBoundingClientRect();
    } else {
      throw new Error('Cannot get BoundingClientRect for SVG.');
    }
  }

  /**
   * Gets g element with class of "viewport" or creates it if it doesn't exist
   *
   * @param  {SVGSVGElement} svg
   * @return {SVGElement}     g (group) element
   */
, getOrCreateViewport: function(svg, selector) {
    var viewport = null

    if (Utils.isElement(selector)) {
      viewport = selector
    } else {
      viewport = svg.querySelector(selector)
    }

    // Check if there is just one main group in SVG
    if (!viewport) {
      var childNodes = Array.prototype.slice.call(svg.childNodes || svg.children).filter(function(el){
        return el.nodeName !== 'defs' && el.nodeName !== '#text'
      })

      // Node name should be SVGGElement and should have no transform attribute
      // Groups with transform are not used as viewport because it involves parsing of all transform possibilities
      if (childNodes.length === 1 && childNodes[0].nodeName === 'g' && childNodes[0].getAttribute('transform') === null) {
        viewport = childNodes[0]
      }
    }

    // If no favorable group element exists then create one
    if (!viewport) {
      var viewportId = 'viewport-' + new Date().toISOString().replace(/\D/g, '');
      viewport = document.createElementNS(this.svgNS, 'g');
      viewport.setAttribute('id', viewportId);

      // Internet Explorer (all versions?) can't use childNodes, but other browsers prefer (require?) using childNodes
      var svgChildren = svg.childNodes || svg.children;
      if (!!svgChildren && svgChildren.length > 0) {
        for (var i = svgChildren.length; i > 0; i--) {
          // Move everything into viewport except defs
          if (svgChildren[svgChildren.length - i].nodeName !== 'defs') {
            viewport.appendChild(svgChildren[svgChildren.length - i]);
          }
        }
      }
      svg.appendChild(viewport);
    }

    // Parse class names
    var classNames = [];
    if (viewport.getAttribute('class')) {
      classNames = viewport.getAttribute('class').split(' ')
    }

    // Set class (if not set already)
    if (!~classNames.indexOf('svg-pan-zoom_viewport')) {
      classNames.push('svg-pan-zoom_viewport')
      viewport.setAttribute('class', classNames.join(' '))
    }

    return viewport
  }

  /**
   * Set SVG attributes
   *
   * @param  {SVGSVGElement} svg
   */
  , setupSvgAttributes: function(svg) {
    // Setting default attributes
    svg.setAttribute('xmlns', this.svgNS);
    svg.setAttributeNS(this.xmlnsNS, 'xmlns:xlink', this.xlinkNS);
    svg.setAttributeNS(this.xmlnsNS, 'xmlns:ev', this.evNS);

    // Needed for Internet Explorer, otherwise the viewport overflows
    if (svg.parentNode !== null) {
      var style = svg.getAttribute('style') || '';
      if (style.toLowerCase().indexOf('overflow') === -1) {
        svg.setAttribute('style', 'overflow: hidden; ' + style);
      }
    }
  }

/**
 * How long Internet Explorer takes to finish updating its display (ms).
 */
, internetExplorerRedisplayInterval: 300

/**
 * Forces the browser to redisplay all SVG elements that rely on an
 * element defined in a 'defs' section. It works globally, for every
 * available defs element on the page.
 * The throttling is intentionally global.
 *
 * This is only needed for IE. It is as a hack to make markers (and 'use' elements?)
 * visible after pan/zoom when there are multiple SVGs on the page.
 * See bug report: https://connect.microsoft.com/IE/feedback/details/781964/
 * also see svg-pan-zoom issue: https://github.com/ariutta/svg-pan-zoom/issues/62
 */
, refreshDefsGlobal: Utils.throttle(function() {
    var allDefs = document.querySelectorAll('defs');
    var allDefsCount = allDefs.length;
    for (var i = 0; i < allDefsCount; i++) {
      var thisDefs = allDefs[i];
      thisDefs.parentNode.insertBefore(thisDefs, thisDefs);
    }
  }, this.internetExplorerRedisplayInterval)

  /**
   * Sets the current transform matrix of an element
   *
   * @param {SVGElement} element
   * @param {SVGMatrix} matrix  CTM
   * @param {SVGElement} defs
   */
, setCTM: function(element, matrix, defs) {
    var that = this
      , s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')';

    if ('transform' in element.style) {
      element.style.transform = s;
    } else if ('-ms-transform' in element.style) {
      element.style['-ms-transform'] = s;
    } else if ('-webkit-transform' in element.style) {
      element.style['-webkit-transform'] = s;
    }

    // IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
    // see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
    // and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
    if (_browser === 'ie' && !!defs) {
      // this refresh is intended for redisplaying the SVG during zooming
      defs.parentNode.insertBefore(defs, defs);
      // this refresh is intended for redisplaying the other SVGs on a page when panning a given SVG
      // it is also needed for the given SVG itself, on zoomEnd, if the SVG contains any markers that
      // are located under any other element(s).
      window.setTimeout(function() {
        that.refreshDefsGlobal();
      }, that.internetExplorerRedisplayInterval);
    }
  }

  /**
   * Instantiate an SVGPoint object with given event coordinates
   *
   * @param {Event} evt
   * @param  {SVGSVGElement} svg
   * @return {SVGPoint}     point
   */
, getEventPoint: function(evt, svg) {
    var point = svg.createSVGPoint()

    Utils.mouseAndTouchNormalize(evt, svg)

    point.x = evt.clientX
    point.y = evt.clientY

    return point
  }

  /**
   * Get SVG center point
   *
   * @param  {SVGSVGElement} svg
   * @return {SVGPoint}
   */
, getSvgCenterPoint: function(svg, width, height) {
    return this.createSVGPoint(svg, width / 2, height / 2)
  }

  /**
   * Create a SVGPoint with given x and y
   *
   * @param  {SVGSVGElement} svg
   * @param  {Number} x
   * @param  {Number} y
   * @return {SVGPoint}
   */
, createSVGPoint: function(svg, x, y) {
    var point = svg.createSVGPoint()
    point.x = x
    point.y = y

    return point
  }
}
<commit_msg>Fix for browsers that don't work well with CSS transforms<commit_after>var Utils = require('./utilities')
  , _browser = 'unknown'
  ;

// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
if (/*@cc_on!@*/false || !!document.documentMode) { // internet explorer
  _browser = 'ie';
}

module.exports = {
  svgNS:  'http://www.w3.org/2000/svg'
, xmlNS:  'http://www.w3.org/XML/1998/namespace'
, xmlnsNS:  'http://www.w3.org/2000/xmlns/'
, xlinkNS:  'http://www.w3.org/1999/xlink'
, evNS:  'http://www.w3.org/2001/xml-events'

  /**
   * Get svg dimensions: width and height
   *
   * @param  {SVGSVGElement} svg
   * @return {Object}     {width: 0, height: 0}
   */
, getBoundingClientRectNormalized: function(svg) {
    if (svg.clientWidth && svg.clientHeight) {
      return {width: svg.clientWidth, height: svg.clientHeight}
    } else if (!!svg.getBoundingClientRect()) {
      return svg.getBoundingClientRect();
    } else {
      throw new Error('Cannot get BoundingClientRect for SVG.');
    }
  }

  /**
   * Gets g element with class of "viewport" or creates it if it doesn't exist
   *
   * @param  {SVGSVGElement} svg
   * @return {SVGElement}     g (group) element
   */
, getOrCreateViewport: function(svg, selector) {
    var viewport = null

    if (Utils.isElement(selector)) {
      viewport = selector
    } else {
      viewport = svg.querySelector(selector)
    }

    // Check if there is just one main group in SVG
    if (!viewport) {
      var childNodes = Array.prototype.slice.call(svg.childNodes || svg.children).filter(function(el){
        return el.nodeName !== 'defs' && el.nodeName !== '#text'
      })

      // Node name should be SVGGElement and should have no transform attribute
      // Groups with transform are not used as viewport because it involves parsing of all transform possibilities
      if (childNodes.length === 1 && childNodes[0].nodeName === 'g' && childNodes[0].getAttribute('transform') === null) {
        viewport = childNodes[0]
      }
    }

    // If no favorable group element exists then create one
    if (!viewport) {
      var viewportId = 'viewport-' + new Date().toISOString().replace(/\D/g, '');
      viewport = document.createElementNS(this.svgNS, 'g');
      viewport.setAttribute('id', viewportId);

      // Internet Explorer (all versions?) can't use childNodes, but other browsers prefer (require?) using childNodes
      var svgChildren = svg.childNodes || svg.children;
      if (!!svgChildren && svgChildren.length > 0) {
        for (var i = svgChildren.length; i > 0; i--) {
          // Move everything into viewport except defs
          if (svgChildren[svgChildren.length - i].nodeName !== 'defs') {
            viewport.appendChild(svgChildren[svgChildren.length - i]);
          }
        }
      }
      svg.appendChild(viewport);
    }

    // Parse class names
    var classNames = [];
    if (viewport.getAttribute('class')) {
      classNames = viewport.getAttribute('class').split(' ')
    }

    // Set class (if not set already)
    if (!~classNames.indexOf('svg-pan-zoom_viewport')) {
      classNames.push('svg-pan-zoom_viewport')
      viewport.setAttribute('class', classNames.join(' '))
    }

    return viewport
  }

  /**
   * Set SVG attributes
   *
   * @param  {SVGSVGElement} svg
   */
  , setupSvgAttributes: function(svg) {
    // Setting default attributes
    svg.setAttribute('xmlns', this.svgNS);
    svg.setAttributeNS(this.xmlnsNS, 'xmlns:xlink', this.xlinkNS);
    svg.setAttributeNS(this.xmlnsNS, 'xmlns:ev', this.evNS);

    // Needed for Internet Explorer, otherwise the viewport overflows
    if (svg.parentNode !== null) {
      var style = svg.getAttribute('style') || '';
      if (style.toLowerCase().indexOf('overflow') === -1) {
        svg.setAttribute('style', 'overflow: hidden; ' + style);
      }
    }
  }

/**
 * How long Internet Explorer takes to finish updating its display (ms).
 */
, internetExplorerRedisplayInterval: 300

/**
 * Forces the browser to redisplay all SVG elements that rely on an
 * element defined in a 'defs' section. It works globally, for every
 * available defs element on the page.
 * The throttling is intentionally global.
 *
 * This is only needed for IE. It is as a hack to make markers (and 'use' elements?)
 * visible after pan/zoom when there are multiple SVGs on the page.
 * See bug report: https://connect.microsoft.com/IE/feedback/details/781964/
 * also see svg-pan-zoom issue: https://github.com/ariutta/svg-pan-zoom/issues/62
 */
, refreshDefsGlobal: Utils.throttle(function() {
    var allDefs = document.querySelectorAll('defs');
    var allDefsCount = allDefs.length;
    for (var i = 0; i < allDefsCount; i++) {
      var thisDefs = allDefs[i];
      thisDefs.parentNode.insertBefore(thisDefs, thisDefs);
    }
  }, this.internetExplorerRedisplayInterval)

  /**
   * Sets the current transform matrix of an element
   *
   * @param {SVGElement} element
   * @param {SVGMatrix} matrix  CTM
   * @param {SVGElement} defs
   */
, setCTM: function(element, matrix, defs) {
    var that = this
      , s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')';

    element.setAttributeNS(null, 'transform', s);
    if ('transform' in element.style) {
      element.style.transform = s;
    } else if ('-ms-transform' in element.style) {
      element.style['-ms-transform'] = s;
    } else if ('-webkit-transform' in element.style) {
      element.style['-webkit-transform'] = s;
    }

    // IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
    // see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
    // and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
    if (_browser === 'ie' && !!defs) {
      // this refresh is intended for redisplaying the SVG during zooming
      defs.parentNode.insertBefore(defs, defs);
      // this refresh is intended for redisplaying the other SVGs on a page when panning a given SVG
      // it is also needed for the given SVG itself, on zoomEnd, if the SVG contains any markers that
      // are located under any other element(s).
      window.setTimeout(function() {
        that.refreshDefsGlobal();
      }, that.internetExplorerRedisplayInterval);
    }
  }

  /**
   * Instantiate an SVGPoint object with given event coordinates
   *
   * @param {Event} evt
   * @param  {SVGSVGElement} svg
   * @return {SVGPoint}     point
   */
, getEventPoint: function(evt, svg) {
    var point = svg.createSVGPoint()

    Utils.mouseAndTouchNormalize(evt, svg)

    point.x = evt.clientX
    point.y = evt.clientY

    return point
  }

  /**
   * Get SVG center point
   *
   * @param  {SVGSVGElement} svg
   * @return {SVGPoint}
   */
, getSvgCenterPoint: function(svg, width, height) {
    return this.createSVGPoint(svg, width / 2, height / 2)
  }

  /**
   * Create a SVGPoint with given x and y
   *
   * @param  {SVGSVGElement} svg
   * @param  {Number} x
   * @param  {Number} y
   * @return {SVGPoint}
   */
, createSVGPoint: function(svg, x, y) {
    var point = svg.createSVGPoint()
    point.x = x
    point.y = y

    return point
  }
}
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */

"use strict";

const stepImpls = require("./stepImplementation");
const endpointImpls = require("./endpointImplementation");
const progressReporter = require("./progressReporter");

const RootStepOrFlow = {
	toString: function () {
		return this.name;
	},
	toJSON: function () {
		return {
			name: this.name
		};
	}
};

exports.create = function (definition, pr) {
	if (!pr) {
		pr = progressReporter.defaultProgressReporter();
	}

	const flowName = definition.name;
	const steps = {};

	pr.pushScope('flow', {
		flow: flowName
	});

	for (const stepName in definition.steps) {
		pr.pushScope('step', {
			step: stepName
		});

		const stepDef = definition.steps[stepName];
		const stepImpl = stepImpls.implementations[stepDef.type];

		if (!stepImpl) {
			pr.error('Step ${type} implementation not found', {
				type: stepDef.type
			});
			break;
		}

		const endpoints = {};
		for (const endpointName in stepImpl.endpoints) {
			//console.log(`Create: ${stepName}/${endpointName}`);

			const eImpl = stepImpl.endpoints[endpointName];
			const eDef = stepDef.endpoints[endpointName];
			const properties = {};

			if (eDef) {
				if (typeof (eDef) === "function") {
					properties.implementation = {
						value: eDef
					};
				} else {
					properties.value = {
						value: eDef
					};
				}
			} else {
				if (eImpl.mandatory) {
					pr.error('Mandatory ${endpoint} not defined', {
						endpoint: endpointName
					});
				}
			}

			endpoints[endpointName] = Object.create(eImpl, properties);
		}

		const config = stepDef.config;
		steps[stepName] = Object.create(RootStepOrFlow, {
			name: {
				value: stepName
			},
			endpoints: {
				value: endpoints
			},
			config: {
				value: config
			},
			meta: {
				value: stepImpl
			}
		});

		pr.popScope();
	}

	for (let stepName in steps) {
		const step = steps[stepName];

		for (const endpointName in step.endpoints) {
			const endpoint = step.endpoints[endpointName];

			// code already assigned -> nothing more to do
			if (!endpoint.implementation) {
				let m = endpoint.value.match(/^([a-z][a-z0-9_]*):?(.*)/);

				if (m) {
					const scheme = m[1];
					const suffix = m[2];
					const ei = endpointImpls.implementations[scheme];
					if (ei) {
						const eimpl = ei.implementation(endpoint);

						step.endpoints[endpointName] = Object.create(endpoint, {
							implementation: {
								value: eimpl
							}
						});
					} else {
						if (scheme === 'step') {
							if ((m = suffix.match(/^([^\/]+)\/(.+)/))) {
								const targetStepName = m[1];
								const targetEndpointName = m[2];
								const targetStep = steps[targetStepName];
								if (targetStep) {
									const targetEndpoint = targetStep.endpoints[targetEndpointName];

									//console.log(`${suffix} -> ${targetStep.name}/ ${targetEndpoint}`);

									const theChain = function* () {
										let request = undefined;

										while (true) {
											if (request) {
												console.log(
													`provide: ${JSON.stringify(request.info)} ${request.stream ? true : false}`
												);
												yield request;
											} else {
												request =
													yield;

												if (request) {
													console.log(
														`got: ${JSON.stringify(request.info)} ${request.stream ? true : false}`
													);
												} else {
													console.log(
														`got: EMPTY`
													);
												}
											}
										}
									};

									step.endpoints[endpointName] = Object.create(endpoint, {
										description: {
											value: `internal connection to ${targetStep}/${targetEndpoint}`
										},
										implementation: {
											value: theChain
										}
									});
									if (targetEndpoint) {
										//console.log(`add implementation: ${targetStepName}/${targetEndpointName}`);
										targetStep.endpoints[targetEndpointName] = Object.create(targetEndpoint, {
											description: {
												value: `internal connection to ${stepName}/${endpointName}`
											},
											implementation: {
												value: theChain
											}
										});
									} else {
										console.log('new endpoint');
									}
									break;
								} else {
									pr.error('Unknown target step: ${step}', {
										step: targetStepName
									});
								}
							}
						}
						pr.error('No implementation for endpoint ${endpoint}', {
							endpoint: ed.value
						});
					}
				}
			}
		}
	}

	const myFlow = Object.create(RootStepOrFlow, {
		name: {
			value: flowName
		},
		steps: {
			value: steps
		},
		initialize: {
			value: function (manager, parentStep) {
				for (let sid in this.steps) {
					let step = this.steps[sid];

					/*
					          const en = [];
					          for (let e in step.endpoints) {
					            const ep = step.endpoints[e];
					            en.push(
					              `${JSON.stringify(ep)}`
					            );
					          }

					          console.log(
					            `initialize: ${step.name} ${en.join(',')}`
					          );
					*/

					//  try {
					step.meta.initialize(manager, step);
					/*    } catch (e) {
					      console.log(`${step.name}: ${e}`);
					    }*/
				}
			}
		}
	});

	return myFlow;
};
<commit_msg>typos<commit_after>/* jslint node: true, esnext: true */

"use strict";

const stepImpls = require("./stepImplementation");
const endpointImpls = require("./endpointImplementation");
const progressReporter = require("./progressReporter");

const RootStepOrFlow = {
	toString: function () {
		return this.name;
	},
	toJSON: function () {
		return {
			name: this.name
		};
	}
};

exports.create = function (definition, pr) {
	if (!pr) {
		pr = progressReporter.defaultProgressReporter();
	}

	const flowName = definition.name;
	const steps = {};

	pr.pushScope('flow', {
		flow: flowName
	});

	for (let stepName in definition.steps) {
		pr.pushScope('step', {
			step: stepName
		});

		const stepDef = definition.steps[stepName];
		const stepImpl = stepImpls.implementations[stepDef.type];

		if (!stepImpl) {
			pr.error('Step ${type} implementation not found', {
				type: stepDef.type
			});
			break;
		}

		const endpoints = {};
		for (let endpointName in stepImpl.endpoints) {
			//console.log(`Create: ${stepName}/${endpointName}`);

			const eImpl = stepImpl.endpoints[endpointName];
			const eDef = stepDef.endpoints[endpointName];
			const properties = {};

			if (eDef) {
				if (typeof (eDef) === "function") {
					properties.implementation = {
						value: eDef
					};
				} else {
					properties.value = {
						value: eDef
					};
				}
			} else {
				if (eImpl.mandatory) {
					pr.error('Mandatory ${endpoint} not defined', {
						endpoint: endpointName
					});
				}
			}

			endpoints[endpointName] = Object.create(eImpl, properties);
		}

		const config = stepDef.config;
		steps[stepName] = Object.create(RootStepOrFlow, {
			name: {
				value: stepName
			},
			endpoints: {
				value: endpoints
			},
			config: {
				value: config
			},
			meta: {
				value: stepImpl
			}
		});

		pr.popScope();
	}

	for (let stepName in steps) {
		const step = steps[stepName];

		for (let endpointName in step.endpoints) {
			const endpoint = step.endpoints[endpointName];

			// code already assigned -> nothing more to do
			if (!endpoint.implementation) {
				let m = endpoint.value.match(/^([a-z][a-z0-9_]*):?(.*)/);

				if (m) {
					const scheme = m[1];
					const suffix = m[2];
					const ei = endpointImpls.implementations[scheme];
					if (ei) {
						const eimpl = ei.implementation(endpoint);

						step.endpoints[endpointName] = Object.create(endpoint, {
							implementation: {
								value: eimpl
							}
						});
					} else {
						if (scheme === 'step') {
							if ((m = suffix.match(/^([^\/]+)\/(.+)/))) {
								const targetStepName = m[1];
								const targetEndpointName = m[2];
								const targetStep = steps[targetStepName];
								if (targetStep) {
									const targetEndpoint = targetStep.endpoints[targetEndpointName];

									//console.log(`${suffix} -> ${targetStep.name}/ ${targetEndpoint}`);

									const theChain = function* () {
										let request;

										while (true) {
											if (request) {
												console.log(
													`provide: ${JSON.stringify(request.info)} ${request.stream ? true : false}`
												);
												yield request;
											} else {
												request =
													yield;

												if (request) {
													console.log(
														`got: ${JSON.stringify(request.info)} ${request.stream ? true : false}`
													);
												} else {
													console.log(
														`got: EMPTY`
													);
												}
											}
										}
									};

									step.endpoints[endpointName] = Object.create(endpoint, {
										description: {
											value: `internal connection to ${targetStep}/${targetEndpoint}`
										},
										implementation: {
											value: theChain
										}
									});
									if (targetEndpoint) {
										//console.log(`add implementation: ${targetStepName}/${targetEndpointName}`);
										targetStep.endpoints[targetEndpointName] = Object.create(targetEndpoint, {
											description: {
												value: `internal connection to ${stepName}/${endpointName}`
											},
											implementation: {
												value: theChain
											}
										});
									} else {
										console.log('new endpoint');
									}
									break;
								} else {
									pr.error('Unknown target step: ${step}', {
										step: targetStepName
									});
								}
							}
						}
						pr.error('No implementation for endpoint ${endpoint}', {
							endpoint: endpoint.value
						});
					}
				}
			}
		}
	}

	const myFlow = Object.create(RootStepOrFlow, {
		name: {
			value: flowName
		},
		steps: {
			value: steps
		},
		initialize: {
			value: function (manager, parentStep) {
				for (let sid in this.steps) {
					let step = this.steps[sid];

					/*
					          const en = [];
					          for (let e in step.endpoints) {
					            const ep = step.endpoints[e];
					            en.push(
					              `${JSON.stringify(ep)}`
					            );
					          }

					          console.log(
					            `initialize: ${step.name} ${en.join(',')}`
					          );
					*/

					//  try {
					step.meta.initialize(manager, step);
					/*    } catch (e) {
					      console.log(`${step.name}: ${e}`);
					    }*/
				}
			}
		}
	});

	return myFlow;
};
<|endoftext|>


<commit_before>import mongoose         from 'mongoose-fill';
import { modelCleaner } from '../../db/utils';

const ObjectId = mongoose.Schema.Types.ObjectId;

const AttachmentSchema = new mongoose.Schema({
  url: {
    type:     String
  },
  filetype: {
    type:     String
  },
  filename: {
    type:     String
  },
  size: {
    type:     Number
  }
}, {
  timestamps: {
    createdAt: 'createdAt',
    updatedAt: 'updatedAt'
  }
});

modelCleaner(AttachmentSchema);
export default mongoose.model('Attachment', AttachmentSchema);
<commit_msg>FileType changed to contentType<commit_after>import mongoose         from 'mongoose-fill';
import { modelCleaner } from '../../db/utils';

const ObjectId = mongoose.Schema.Types.ObjectId;

const AttachmentSchema = new mongoose.Schema({
  url: {
    type:     String
  },
  contentType: {
    type:     String
  },
  filename: {
    type:     String
  },
  size: {
    type:     Number
  }
}, {
  timestamps: {
    createdAt: 'createdAt',
    updatedAt: 'updatedAt'
  }
});

modelCleaner(AttachmentSchema);
export default mongoose.model('Attachment', AttachmentSchema);
<|endoftext|>


<commit_before>const fs = require('fs')

const { ConfigurationFileNotExist } = require('./config-file-not-exist.error')
const { FsUtils } = require('../../shared/fs.utils')

class ConfigService {
  get repoUrl() {
    return this.get('repo_url')
  }

  set repoUrl(url) {
    this.set('repo_url', url)
  }

  get folderPath() {
    return this.get('folder_path')
  }

  set folderPath(path) {
    this.set('folder_path', path)
  }

  get scriptExtension() {
    const extentions = this.get('script_extensions')

    if (extentions === null) {
      return ['.js', '.sh', '']
    }

    return extentions
  }

  set scriptExtension(extentions) {
    if (Array.isArray(extentions)) {
      this.set('script_extensions', extentions)
    }
  }

  constructor(configPath = `${process.env.HOME}/.configfile`) {
    this.configPath = `${configPath}rc`
    this.configFolderPath = `${configPath}`
  }

  configFileExist() {
    return FsUtils.fileExist(this.configPath)
  }

  configData() {
    if (!this.configFileExist()) {
      throw new ConfigurationFileNotExist()
    }

    return JSON.parse(fs.readFileSync(this.configPath))
  }

  get(key) {
    const configData = this.configData()

    if (key in configData) {
      return configData[key]
    }

    return null
  }

  set(key, value) {
    let configData = null

    try {
      configData = this.configData()
    } catch (e) {
      if (!(e instanceof ConfigurationFileNotExist)) {
        throw e
      }

      configData = {}
    }

    configData[key] = value

    fs.writeFileSync(this.configPath, JSON.stringify(configData, null, 4))
  }
}

module.exports = exports = { ConfigService }
<commit_msg>Rename get and set generic function<commit_after>const fs = require('fs')

const { ConfigurationFileNotExist } = require('./config-file-not-exist.error')
const { FsUtils } = require('../../shared/fs.utils')

class ConfigService {
  get repoUrl() {
    return this.getConfig('repo_url')
  }

  set repoUrl(url) {
    this.setConfig('repo_url', url)
  }

  get folderPath() {
    return this.getConfig('folder_path')
  }

  set folderPath(path) {
    this.setConfig('folder_path', path)
  }

  get scriptExtension() {
    const extentions = this.getConfig('script_extensions')

    if (extentions === null) {
      return ['.js', '.sh', '']
    }

    return extentions
  }

  set scriptExtension(extentions) {
    if (Array.isArray(extentions)) {
      this.setConfig('script_extensions', extentions)
    }
  }

  constructor(configPath = `${process.env.HOME}/.configfile`) {
    this.configPath = `${configPath}rc`
    this.configFolderPath = `${configPath}`

    if (!FsUtils.fileExist(this.configFolderPath)) {
      fs.mkdirSync(this.configFolderPath)
    }
  }

  configFileExist() {
    return FsUtils.fileExist(this.configPath)
  }

  configData() {
    if (!this.configFileExist()) {
      throw new ConfigurationFileNotExist()
    }

    return JSON.parse(fs.readFileSync(this.configPath))
  }

  getConfig(key) {
    const configData = this.configData()

    if (key in configData) {
      return configData[key]
    }

    return null
  }

  setConfig(key, value) {
    let configData = null

    try {
      configData = this.configData()
    } catch (e) {
      if (!(e instanceof ConfigurationFileNotExist)) {
        throw e
      }

      configData = {}
    }

    configData[key] = value

    fs.writeFileSync(this.configPath, JSON.stringify(configData, null, 4))
  }
}

module.exports = exports = { ConfigService }
<|endoftext|>


<commit_before><commit_msg>tighter pattern<commit_after><|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var request = require('request');
var events = require('events');
var fh = require('fh-mbaas-api');

var fuseRoute = function() {
  var fuse = new express.Router();
  fuse.use(cors());
  fuse.use(bodyParser());

  fuse.post('/', function(req, res) {
    console.log(new Date(), 'In fuse route POST / req.body=', req.body);
    return submitToFuse(req.body, function(error, response, body){
      return res.json(body);
    });
  });

  return fuse;
};

var submitToFusefunction(submission, cb){
  var url = process.env.FUSE_URL;
  request.post(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log(body) // Show the HTML
    }
    cb(error, response, body);
  })
};

var submissionEventListener = new events.EventEmitter();

submissionEventListener.on('submissionComplete', function(submission){
  console.log('New submission received: ');
  console.log(submission);
  return submitToFuseSubmission, function(error, response, body){
      console.log('Received response from Fuse in event handler: ' + response.statusCode);
      console.log(body);
  });
});

fh.forms.registerListener(submissionEventListener, function(err){});

module.exports = fuseRoute;<commit_msg>update file /lib/fuse.js<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var request = require('request');
var events = require('events');
var fh = require('fh-mbaas-api');

var fuseRoute = function() {
  var fuse = new express.Router();
  fuse.use(cors());
  fuse.use(bodyParser());

  fuse.post('/', function(req, res) {
    console.log(new Date(), 'In fuse route POST / req.body=', req.body);
    return submitToFuse(req.body, function(error, response, body){
      return res.json(body);
    });
  });

  return fuse;
};

var submitToFuse = function(submission, cb){
  var url = process.env.FUSE_URL;
  request.post(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log(body) // Show the HTML
    }
    cb(error, response, body);
  })
};

var submissionEventListener = new events.EventEmitter();

submissionEventListener.on('submissionComplete', function(submission){
  console.log('New submission received: ');
  console.log(submission);
  return submitToFuseSubmission, function(error, response, body){
      console.log('Received response from Fuse in event handler: ' + response.statusCode);
      console.log(body);
  });
});

fh.forms.registerListener(submissionEventListener, function(err){});

module.exports = fuseRoute;<|endoftext|>


<commit_before>var path = require('path'),
    fs = require('fs'),
    External = require('./external'),
    Step = require('step'),
    _ = require('underscore')._,
    sys = require('sys');

require.paths.unshift(path.join(__dirname, '..', 'lib'));

var mess = require('mess');

/**
 * Convert a two-element-per-item Array
 * into an object, for the purpose of checking membership
 * and replacing stuff.
 * @param {Array} list a list.
 */
var to = function(list) {
    return list && list[0] && _.reduce(list, function(m, r) {
        if (r && r.length > 1) {
          m[r[0]] = r[1];
        }
        return m;
    }, {});
};

mess.Renderer = function Renderer(env) {
    var env = {
        data_dir: env.data_dir || '/tmp/'
    };
    return {
        env: env,
        /**
         * Wrapper for downloading externals: likely removable
         *
         * @param {String} uri the URI of the resource.
         * @param {Function} callback
         */
        grab: function(uri, callback) {
            new(External)(this.env).process(uri, callback);
        },

        /**
         * Download any file-based remote datsources.
         *
         * Usable as an entry point: does not expect any modification to
         * the map object beyond JSON parsing.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeExternals: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Layer.forEach(function(l) {
                        if (l.Datasource.file) {
                            that.grab(l.Datasource.file, group());
                        }
                    });
                },
                function(err, results) {
                    var result_map = to(results);
                    m.Layer.forEach(function(l) {
                        if (l.Datasource.file) {
                            l.Datasource.file = result_map[l.Datasource.file];
                        }
                    });
                    callback(err, m);
                }
            );
        },

        /**
         * Download any remote stylesheets
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeStyle: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (!s.id) {
                            that.grab(s, group());
                        }
                    });
                    group()();
                },
                function(err, results) {
                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        }
                    }
                    callback(err, m);
                }
            );
        },

        /**
         * Compile (already downloaded) styles with mess.js,
         * calling callback with an array of [map object, [stylesheet objects]]
         *
         * Called with the results of localizeStyle or localizeExternals:
         * expects not to handle downloading.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        style: function(m, callback) {
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (s.id) {
                            group()(null, [s.id, s.data]);
                        } else {
                            fs.readFile(s, 'utf-8', function(err, data) {
                                group()(err, [s, data]);
                            });
                        }
                    });
                },
                function(e, results) {
                    var options = {},
                        group = this.group();
                    for (var i = 0, l = results.length; i < l; i++) {
                        new(mess.Parser)({
                            filename: s
                        }).parse(results[i][1], function(err, tree) {
                            if (err) {
                                mess.writeError(err, options);
                                throw err;
                            } else {
                                try {
                                    group()(err, [
                                        results[i][0],
                                        tree.toCSS({ compress: false }),
                                        tree]);
                                    return;
                                } catch (e) {
                                    throw e;
                                    return;
                                }
                            }
                        });
                    }
                },
                function(err, results) {
                    if (err) {
                        callback(err);
                        return;
                    }

                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        } else {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s].id];
                        }
                    }
                    callback(err, [m, results]);
                }
            );
        },

        /**
         * Prepare full XML map output. Called with the results
         * of this.style
         *
         * @param {Array} res array of [map object, stylesheets].
         * @param {Function} callback
         */
        template: function(err, res, callback) {
            if (err) {
                callback(err);
                return;
            }

            var m = res[0],
                stylesheets = res[1],

                entities = _.template('<?xml version="1.0" ' +
                'encoding="utf-8"?>\n' +
                '<!DOCTYPE Map[\n' +
                '<% for (ref in externals) { %>' +
                '  <!ENTITY <%= ref %> SYSTEM "<%= externals[ref] %>">\n' +
                '<% } %>]>\n'),

                styles = _.template('<StyleName><%= name %></StyleName>'),

                layer = _.template('  <Layer\n id="<%= id %>"\n ' +
                '   name="<%= name %>">\n' +
                '   srs="<%= srs %>">\n' +
                '<% for (s in styles) { %>' +
                '    <StyleName><%= styles[s] %></StyleName>\n' +
                '<% } %>' +
                '    <Datasource>\n<% for (n in Datasource) { %>' +
                '      <Parameter name="<%= n %>"><%= Datasource[n] %>' +
                '</Parameter>\n' +
                '<% } %>  </Datasource>\n' +
                '  </Layer>\n'),

                stylesheet_tmpl = _.template('  <% for (i in Stylesheet) { %>' +
                    '<%= Stylesheet[i] %>\n' +
                    '<% } %>\n'),

                // referencing externals in the body of the map
                references = _.template('<% for (ref in externals) { %>' +
                '&<%= ref %>;\n' +
                '<% } %>'),

                // for the Map[] doctype: XML entities to
                // organize code
                entity_list = { externals: [], symbols: [] };

            /**
             * Index a hash of the CSS rules in all stylesheets
             * This is performance-critical: necessary to test
             * at some point
             *
             * @param {Array} stylesheets
             * @return {Array} list of selectors.
             */
            var ruleHash = function(stylesheets) {
                return _.flatten(_.map(stylesheets, function(s) {
                    return _.map(s[2].rules, function(r) {
                        if (r && r.selectors && r.selectors[0]._css) {
                            return r.selectors[0]._css;
                        }
                    });
                }));
            };

            /**
             * Add styles to a layer object
             *
             * @param {Object} layer the layer object.
             * @param {Array} stylesheets the results of ruleHash().
             * @return Formalized layer definition with .styles = [].
             */
            var styleIntersect = function(layer, stylesheets) {
                var hash = ruleHash(stylesheets);
                layer.styles = [];
                if (_.include(hash, 'id-' + layer.id)) {
                    layer.styles.push('id-' + layer.id);
                }
                return layer;
            };

            var output = [entities(entity_list)];
            // TODO: must change when background colors are available
            output.push('<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs" background-color="#ffffff">\n');
            // using array joins here: faster in IE7, unclear in general.
            m.Layer.forEach(function(l) {
                l = styleIntersect(l, stylesheets);
                output.push(layer(l));
            });
            output.push(references(entity_list));
            output.push(stylesheet_tmpl(m));
            output.push('</Map>');
            callback(null, output.join(''));
        },

        /**
         * Prepare a JML document (given as a string) into a
         * fully-localized XML file ready for Mapnik2 consumption
         *
         * @param {String} str the JSON file as a string.
         * @param {Function} callback to be called with err, XML representation.
         */
        render: function(str, callback) {
            var m = JSON.parse(str),
                that = this;
            this.localizeExternals(m, function(err, res) {
                that.localizeStyle(res, function(err, res) {
                    that.style(res, function(err, res) {
                        that.template(err, res, function(err, res) {
                            callback(err, res);
                        });
                    });
                });
            });
        }
    };
};

module.exports = mess;
<commit_msg>Double-closing bracket for layer XML<commit_after>var path = require('path'),
    fs = require('fs'),
    External = require('./external'),
    Step = require('step'),
    _ = require('underscore')._,
    sys = require('sys');

require.paths.unshift(path.join(__dirname, '..', 'lib'));

var mess = require('mess');

/**
 * Convert a two-element-per-item Array
 * into an object, for the purpose of checking membership
 * and replacing stuff.
 * @param {Array} list a list.
 */
var to = function(list) {
    return list && list[0] && _.reduce(list, function(m, r) {
        if (r && r.length > 1) {
          m[r[0]] = r[1];
        }
        return m;
    }, {});
};

mess.Renderer = function Renderer(env) {
    var env = {
        data_dir: env.data_dir || '/tmp/'
    };
    return {
        env: env,
        /**
         * Wrapper for downloading externals: likely removable
         *
         * @param {String} uri the URI of the resource.
         * @param {Function} callback
         */
        grab: function(uri, callback) {
            new(External)(this.env).process(uri, callback);
        },

        /**
         * Download any file-based remote datsources.
         *
         * Usable as an entry point: does not expect any modification to
         * the map object beyond JSON parsing.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeExternals: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Layer.forEach(function(l) {
                        if (l.Datasource.file) {
                            that.grab(l.Datasource.file, group());
                        }
                    });
                },
                function(err, results) {
                    var result_map = to(results);
                    m.Layer.forEach(function(l) {
                        if (l.Datasource.file) {
                            l.Datasource.file = result_map[l.Datasource.file];
                        }
                    });
                    callback(err, m);
                }
            );
        },

        /**
         * Download any remote stylesheets
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeStyle: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (!s.id) {
                            that.grab(s, group());
                        }
                    });
                    group()();
                },
                function(err, results) {
                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        }
                    }
                    callback(err, m);
                }
            );
        },

        /**
         * Compile (already downloaded) styles with mess.js,
         * calling callback with an array of [map object, [stylesheet objects]]
         *
         * Called with the results of localizeStyle or localizeExternals:
         * expects not to handle downloading.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        style: function(m, callback) {
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (s.id) {
                            group()(null, [s.id, s.data]);
                        } else {
                            fs.readFile(s, 'utf-8', function(err, data) {
                                group()(err, [s, data]);
                            });
                        }
                    });
                },
                function(e, results) {
                    var options = {},
                        group = this.group();
                    for (var i = 0, l = results.length; i < l; i++) {
                        new(mess.Parser)({
                            filename: s
                        }).parse(results[i][1], function(err, tree) {
                            if (err) {
                                mess.writeError(err, options);
                                throw err;
                            } else {
                                try {
                                    group()(err, [
                                        results[i][0],
                                        tree.toCSS({ compress: false }),
                                        tree]);
                                    return;
                                } catch (e) {
                                    throw e;
                                    return;
                                }
                            }
                        });
                    }
                },
                function(err, results) {
                    if (err) {
                        callback(err);
                        return;
                    }

                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        } else {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s].id];
                        }
                    }
                    callback(err, [m, results]);
                }
            );
        },

        /**
         * Prepare full XML map output. Called with the results
         * of this.style
         *
         * @param {Array} res array of [map object, stylesheets].
         * @param {Function} callback
         */
        template: function(err, res, callback) {
            if (err) {
                callback(err);
                return;
            }

            var m = res[0],
                stylesheets = res[1],

                entities = _.template('<?xml version="1.0" ' +
                'encoding="utf-8"?>\n' +
                '<!DOCTYPE Map[\n' +
                '<% for (ref in externals) { %>' +
                '  <!ENTITY <%= ref %> SYSTEM "<%= externals[ref] %>">\n' +
                '<% } %>]>\n'),

                styles = _.template('<StyleName><%= name %></StyleName>'),

                layer = _.template('  <Layer\n id="<%= id %>"\n ' +
                '   name="<%= name %>"\n' +
                '   srs="<%= srs %>">\n' +
                '<% for (s in styles) { %>' +
                '    <StyleName><%= styles[s] %></StyleName>\n' +
                '<% } %>' +
                '    <Datasource>\n<% for (n in Datasource) { %>' +
                '      <Parameter name="<%= n %>"><%= Datasource[n] %>' +
                '</Parameter>\n' +
                '<% } %>  </Datasource>\n' +
                '  </Layer>\n'),

                stylesheet_tmpl = _.template('  <% for (i in Stylesheet) { %>' +
                    '<%= Stylesheet[i] %>\n' +
                    '<% } %>\n'),

                // referencing externals in the body of the map
                references = _.template('<% for (ref in externals) { %>' +
                '&<%= ref %>;\n' +
                '<% } %>'),

                // for the Map[] doctype: XML entities to
                // organize code
                entity_list = { externals: [], symbols: [] };

            /**
             * Index a hash of the CSS rules in all stylesheets
             * This is performance-critical: necessary to test
             * at some point
             *
             * @param {Array} stylesheets
             * @return {Array} list of selectors.
             */
            var ruleHash = function(stylesheets) {
                return _.flatten(_.map(stylesheets, function(s) {
                    return _.map(s[2].rules, function(r) {
                        if (r && r.selectors && r.selectors[0]._css) {
                            return r.selectors[0]._css;
                        }
                    });
                }));
            };

            /**
             * Add styles to a layer object
             *
             * @param {Object} layer the layer object.
             * @param {Array} stylesheets the results of ruleHash().
             * @return Formalized layer definition with .styles = [].
             */
            var styleIntersect = function(layer, stylesheets) {
                var hash = ruleHash(stylesheets);
                layer.styles = [];
                if (_.include(hash, 'id-' + layer.id)) {
                    layer.styles.push('id-' + layer.id);
                }
                return layer;
            };

            var output = [entities(entity_list)];
            // TODO: must change when background colors are available
            output.push('<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs" background-color="#ffffff">\n');
            // using array joins here: faster in IE7, unclear in general.
            m.Layer.forEach(function(l) {
                l = styleIntersect(l, stylesheets);
                output.push(layer(l));
            });
            output.push(references(entity_list));
            output.push(stylesheet_tmpl(m));
            output.push('</Map>');
            callback(null, output.join(''));
        },

        /**
         * Prepare a JML document (given as a string) into a
         * fully-localized XML file ready for Mapnik2 consumption
         *
         * @param {String} str the JSON file as a string.
         * @param {Function} callback to be called with err, XML representation.
         */
        render: function(str, callback) {
            var m = JSON.parse(str),
                that = this;
            this.localizeExternals(m, function(err, res) {
                that.localizeStyle(res, function(err, res) {
                    that.style(res, function(err, res) {
                        that.template(err, res, function(err, res) {
                            callback(err, res);
                        });
                    });
                });
            });
        }
    };
};

module.exports = mess;
<|endoftext|>


<commit_before>const Player = require('./player');
const $      = require('jquery');

function createBoard() {
  var canvas    = document.getElementById('game');
  var gameBoard = canvas.getContext("2d");
  var width     = $(canvas).width();
  var height    = $(canvas).height();

  gameBoard.fillStyle = "black";
  gameBoard.fillRect(0, 0, width, height);

  return gameBoard;
}

function Game() {
  this.board = createBoard();
  this.oneKeys = {
    left:  "65",
    up:    "87",
    right: "68",
    down:  "83"
  };
  this.twoKeys = {
    left:  "37",
    up:    "38",
    right: "39",
    down:  "40"
  };
}

Game.prototype.start = function () {
  this.createPlayers();
  window.gameLoopInterval = setInterval(this.movePlayers.bind(this), 30);
};

Game.prototype.createPlayers = function() {
  this.playerOne = new Player("red", 10, 30, "right", this.oneKeys);
  this.playerTwo = new Player("blue", 110, 30, "left", this.twoKeys);
};

Game.prototype.movePlayers = function() {
  this.playerOne.move(this);
  this.playerTwo.move(this);
};

Game.prototype.detectCollision = function(position) {
  this.detectBorderCollision(position);
  this.detectPlayerCollision(position, this.playerOne);
  this.detectPlayerCollision(position, this.playerTwo);
};

Game.prototype.detectBorderCollision = function(position) {
  if (position.x == -1 || position.x == 1200 / 10 || position.y == -1 || position.y == 600 / 10) {
    this.end();
  }
}

Game.prototype.detectPlayerCollision = function(position, player) {
  for (var i = 0; i < player.trail.length; i++) {
    if (position.x === player.trail[i].x && position.y === player.trail[i].y) {
      this.end();
    }
  }
}

Game.prototype.end = function() {
  clearInterval(window.gameLoopInterval);
  document.getElementById("end").style.display = 'inline';

  $(document).keydown(function(k){
    if (k.which == "13") document.location.reload(true);
  });
};

module.exports = Game;
<commit_msg>add logic to reset game so that music will keep playing<commit_after>const Player = require('./player');
const $      = require('jquery');

function createBoard() {
  var canvas    = document.getElementById('game');
  var gameBoard = canvas.getContext("2d");
  var width     = $(canvas).width();
  var height    = $(canvas).height();

  gameBoard.fillStyle = "black";
  gameBoard.fillRect(0, 0, width, height);

  return gameBoard;
}

function Game() {
  this.board = createBoard();
  this.oneKeys = {
    left:  "65",
    up:    "87",
    right: "68",
    down:  "83"
  };
  this.twoKeys = {
    left:  "37",
    up:    "38",
    right: "39",
    down:  "40"
  };
}

Game.prototype.createPlayers = function() {
  this.playerOne = new Player("red", 10, 30, "right", this.oneKeys);
  this.playerTwo = new Player("blue", 110, 30, "left", this.twoKeys);
};

Game.prototype.start = function () {
  this.createPlayers();
  window.gameLoopInterval = setInterval(this.movePlayers.bind(this), 30);
};

Game.prototype.movePlayers = function() {
  this.playerOne.move(this);
  this.playerTwo.move(this);
};

Game.prototype.detectCollision = function(position) {
  this.detectBorderCollision(position);
  this.detectPlayerCollision(position, this.playerOne);
  this.detectPlayerCollision(position, this.playerTwo);
};

Game.prototype.detectBorderCollision = function(position) {
  if (position.x == -1 || position.x == 1200 / 10 || position.y == -1 || position.y == 600 / 10) {
    this.end();
  }
}

Game.prototype.detectPlayerCollision = function(position, player) {
  for (var i = 0; i < player.trail.length; i++) {
    if (position.x === player.trail[i].x && position.y === player.trail[i].y) {
      this.end();
    }
  }
}

Game.prototype.end = function() {

  clearInterval(window.gameLoopInterval);
  document.getElementById("end").style.display = 'inline';

  var game = this;
  $(document).keydown(function(k){
    if (k.which == "13") {
      $('canvas').remove();
      $('body').prepend("<canvas id='game' width='1200' height='600'></canvas>");
      document.getElementById("end").style.display = 'none';
      game.board = createBoard();
      game.start();
      // document.location.reload(true);
    }
  });

};

module.exports = Game;
<|endoftext|>


<commit_before>'use strict';

var serverConfig   = require('../configs/server');
var facebookConfig = require('../configs/facebook');
var _              = require('lodash');

var urlGenerator = {
    user: function(user) {
        return serverConfig.URL + '/users/' + user.id;
    },
    business: function(business) {
        if(business.slug) {
            return serverConfig.URL + '/businesses/' + business.id + '/' + business.slug;
        } else {
            return serverConfig.URL + '/businesses/' + business.id;
        }

    },
    hairfie: function(hairfie) {
        return serverConfig.URL + '/hairfies/' + hairfie.id;
    }
}

var globalMetas = [
    { property: "fb:app_id", content: facebookConfig.APP_ID },
    { property: "og:locale:alternate", content: "fr_FR" }
];

var daysOfWeek = {
    MON: 'monday',
    TUE: 'tuesday',
    WED: 'wednesday',
    THU: 'thursday',
    FRI: 'friday',
    SAT: 'saturday',
    SUN: 'sunday'
};

function fullDayOfWeek(abbr) {
    return daysOfWeek[abbr];
}

module.exports = {
    getBasicMetadata: function() {
        var metas = [];
        metas.push.apply(metas, globalMetas);
        metas.push({property: "og:image", content: serverConfig.URL + '/img/logo@2x.png'});
        metas.push({property: "og:title", content: "Hairfie"});
        metas.push({property: "og:url", content: serverConfig.URL});

        return metas;
    },

    getHairfieMetadata: function (hairfie) {
        var author         = hairfie.author,
            business       = hairfie.business,
            metas          = [],
            canonicalUrl   = urlGenerator.hairfie(hairfie),
            homeUrl        = serverConfig.URL,
            authorUrl      = author ? urlGenerator.user(author) : null,
            businessUrl    = business ? urlGenerator.business(business) : null,
            title          = null,
            hairdresserUrl = null,
            description    = null;

        if(hairfie.hairdresser) {
            title = 'Hairfie réalisé par ' + hairfie.hairdresser.firstName;
            description = 'Réservez gratuitement en ligne avec le même style par ' + hairfie.hairdresser.firstName + ' au salon ' + business.name + ' sur Hairfie';
        } else if(business) {
            title = 'Hairfie réalisé chez ' + business.name;
            description = 'Réservez gratuitement en ligne avec le même style au salon ' + business.name + ' sur Hairfie';

        else {
            title = 'Hairfie posté par ' + author.firstName;
            description = 'Réservez gratuitement votre séance en ligne sur Hairfie'
        }

        title += ' | Hairfie'

        if(hairfie.tags) {
            description += _.map(hairfie.tags, function(tag) { return '#'+tag.name.replace(/ /g,''); }).join(" ");
        }

        metas.push.apply(metas, globalMetas);
        metas.push({property: "og:type", content: facebookConfig.APP_NAMESPACE+':hairfie'});
        metas.push({property: "og:url", content: canonicalUrl});
        metas.push({property: "og:title", content: title});
        metas.push({property: "og:image", content: hairfie.picture.url});
        metas.push({property: "og:description", content: description})

        if (authorUrl) {
            metas.push({property: "hairfie:author", content: authorUrl});
        }
        if (businessUrl) {
            metas.push({property: "hairfie:business", content: businessUrl});
        }
        if (hairdresserUrl) {
            metas.push({property: "hairfie:hairdresser", content: hairdresserUrl});
        }
        return metas;
    },

    getBusinessMetadata: function(business) {
        var title          = 'Réserver un en ligne chez ' + business.name ' sur Hairfie',
            description    = 'Réservez gratuitement en quelques clics sur Hairfie avec ' + business.name,
            metas          = [],
            canonicalUrl   = urlGenerator.business(business),
            homeUrl        = serverConfig.URL;

        metas.push.apply(metas, globalMetas);
        metas.push({property: "og:type", content: facebookConfig.APP_NAMESPACE+':business'});
        metas.push({property: "og:url", content: canonicalUrl});
        metas.push({property: "og:title", content: title});
        metas.push({property: "og:description", content: description});

        if (business.address) {
            metas.push({property: "business:contact_data:street_address", content: business.address.street});
            metas.push({property: "business:contact_data:locality", content: business.address.city});
            metas.push({property: "business:contact_data:postal_code", content: business.address.zipCode});
            metas.push({property: "business:contact_data:country_name", content: business.address.country});
        }

        if (business.timetable) {
            for (var day in business.timetable) {
                var timeWindows = business.timetable[day];

                if (0 == timeWindows.length) continue;

                metas.push({property: "business:hours:day", content: fullDayOfWeek(day)});
                for (var i = 0; i < Math.min(timeWindows.length, 2); i++) {
                    var timeWindow = timeWindows[i];
                    metas.push({property: "business:hours:start", content: timeWindow.startTime});
                    metas.push({property: "business:hours:end", content: timeWindow.endTime});
                }
            }
        }

        if (business.gps) {
            metas.push({property: "place:location:latitude", content: business.gps.lat});
            metas.push({property: "place:location:longitude", content: business.gps.lng});
        }

        business.pictures.map(function (picture) {
            metas.push({property: "og:image", content: picture.url});
        });

        return metas;
    }
};
<commit_msg>change metas<commit_after>'use strict';

var serverConfig   = require('../configs/server');
var facebookConfig = require('../configs/facebook');
var _              = require('lodash');

var urlGenerator = {
    user: function(user) {
        return serverConfig.URL + '/users/' + user.id;
    },
    business: function(business) {
        if(business.slug) {
            return serverConfig.URL + '/businesses/' + business.id + '/' + business.slug;
        } else {
            return serverConfig.URL + '/businesses/' + business.id;
        }

    },
    hairfie: function(hairfie) {
        return serverConfig.URL + '/hairfies/' + hairfie.id;
    }
}

var globalMetas = [
    { property: "fb:app_id", content: facebookConfig.APP_ID },
    { property: "og:locale:alternate", content: "fr_FR" }
];

var daysOfWeek = {
    MON: 'monday',
    TUE: 'tuesday',
    WED: 'wednesday',
    THU: 'thursday',
    FRI: 'friday',
    SAT: 'saturday',
    SUN: 'sunday'
};

function fullDayOfWeek(abbr) {
    return daysOfWeek[abbr];
}

module.exports = {
    getBasicMetadata: function() {
        var metas = [];
        metas.push.apply(metas, globalMetas);
        metas.push({property: "og:image", content: serverConfig.URL + '/img/logo@2x.png'});
        metas.push({property: "og:title", content: "Hairfie"});
        metas.push({property: "og:url", content: serverConfig.URL});

        return metas;
    },

    getHairfieMetadata: function (hairfie) {
        var author         = hairfie.author,
            business       = hairfie.business,
            metas          = [],
            canonicalUrl   = urlGenerator.hairfie(hairfie),
            homeUrl        = serverConfig.URL,
            authorUrl      = author ? urlGenerator.user(author) : null,
            businessUrl    = business ? urlGenerator.business(business) : null,
            title          = null,
            hairdresserUrl = null,
            description    = null;

        if(hairfie.hairdresser) {
            title = 'Hairfie réalisé par ' + hairfie.hairdresser.firstName;
            description = 'Réservez gratuitement en ligne avec le même style par ' + hairfie.hairdresser.firstName + ' au salon ' + business.name + ' sur Hairfie';
        } else if(business) {
            title = 'Hairfie réalisé chez ' + business.name;
            description = 'Réservez gratuitement en ligne avec le même style au salon ' + business.name + ' sur Hairfie';
        } else {
            title = 'Hairfie posté par ' + author.firstName;
            description = 'Réservez gratuitement votre séance en ligne sur Hairfie'
        }

        title += ' | Hairfie'

        if(hairfie.tags) {
            description += _.map(hairfie.tags, function(tag) { return '#'+tag.name.replace(/ /g,''); }).join(" ");
        }

        metas.push.apply(metas, globalMetas);
        metas.push({property: "og:type", content: facebookConfig.APP_NAMESPACE+':hairfie'});
        metas.push({property: "og:url", content: canonicalUrl});
        metas.push({property: "og:title", content: title});
        metas.push({property: "og:image", content: hairfie.picture.url});
        metas.push({property: "og:description", content: description})

        if (authorUrl) {
            metas.push({property: "hairfie:author", content: authorUrl});
        }
        if (businessUrl) {
            metas.push({property: "hairfie:business", content: businessUrl});
        }
        if (hairdresserUrl) {
            metas.push({property: "hairfie:hairdresser", content: hairdresserUrl});
        }
        return metas;
    },

    getBusinessMetadata: function(business) {
        var title          = 'Réserver un en ligne chez ' + business.name + ' sur Hairfie',
            description    = 'Réservez gratuitement en quelques clics sur Hairfie avec ' + business.name,
            metas          = [],
            canonicalUrl   = urlGenerator.business(business),
            homeUrl        = serverConfig.URL;

        metas.push.apply(metas, globalMetas);
        metas.push({property: "og:type", content: facebookConfig.APP_NAMESPACE+':business'});
        metas.push({property: "og:url", content: canonicalUrl});
        metas.push({property: "og:title", content: title});
        metas.push({property: "og:description", content: description});

        if (business.address) {
            metas.push({property: "business:contact_data:street_address", content: business.address.street});
            metas.push({property: "business:contact_data:locality", content: business.address.city});
            metas.push({property: "business:contact_data:postal_code", content: business.address.zipCode});
            metas.push({property: "business:contact_data:country_name", content: business.address.country});
        }

        if (business.timetable) {
            for (var day in business.timetable) {
                var timeWindows = business.timetable[day];

                if (0 == timeWindows.length) continue;

                metas.push({property: "business:hours:day", content: fullDayOfWeek(day)});
                for (var i = 0; i < Math.min(timeWindows.length, 2); i++) {
                    var timeWindow = timeWindows[i];
                    metas.push({property: "business:hours:start", content: timeWindow.startTime});
                    metas.push({property: "business:hours:end", content: timeWindow.endTime});
                }
            }
        }

        if (business.gps) {
            metas.push({property: "place:location:latitude", content: business.gps.lat});
            metas.push({property: "place:location:longitude", content: business.gps.lng});
        }

        business.pictures.map(function (picture) {
            metas.push({property: "og:image", content: picture.url});
        });

        return metas;
    }
};
<|endoftext|>