<commit_before>// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Imports JSON file with the raw payloads from a Windows event
 * trace into the Tracemodel. This format is outputted by Chrome running
 * on a Windows system.
 *
 * This importer assumes the events arrived as a JSON file and the payloads are
 * undecoded sequence of bytes in hex format string. The unit tests provide
 * examples of the trace format.
 *
 * The format of the system trace is
 *     {
 *       name: 'ETW',
 *       content: [ <events> ]
 *     }
  *
 * where the <events> are dictionary values with fields.
 *
 *     {
 *       guid: "1234-...",    // The unique GUID for the event.
 *       op: 12,              // The opcode of the event.
 *       ver: 1,              // The encoding version of the event.
 *       cpu: 0,              // The cpu id on which the event was captured.
 *       ts: 1092,            // The thread id on which the event was captured.
 *       payload: "aaaa"      // A base64 encoded string of the raw payload.
 *     }
 *
 * The payload is an undecoded version of the raw event sent by ETW.
 * This importer uses specific parsers to decode recognized events.
 * A parser need to register the recognized event by calling
 * registerEventHandler(guid, opcode, handler). The parser is responsible to
 * decode the payload and update the TraceModel.
 *
 * The payload formats are described there:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa364085(v=vs.85).aspx
 *
 */
'use strict';

tvcm.require('tvcm.base64');
tvcm.require('tracing.trace_model');
tvcm.require('tracing.importer.importer');
tvcm.require('tracing.importer.etw.process_parser');
tvcm.require('tracing.importer.etw.thread_parser');

tvcm.exportTo('tracing.importer', function() {

  var Importer = tracing.importer.Importer;

  /**
   * Represents the raw bytes payload decoder.
   * @constructor
   */
  function Decoder() {
    this.payload_ = new DataView(new ArrayBuffer(256));
  };

  Decoder.prototype = {
    __proto__: Object.prototype,

    reset: function(base64_payload) {
      var decoded_size = tvcm.Base64.getDecodedBufferLength(base64_payload);
      if (decoded_size > this.payload_.byteLength)
        this.payload_ = new DataView(new ArrayBuffer(decoded_size));

      tvcm.Base64.DecodeToTypedArray(base64_payload, this.payload_);
      this.position_ = 0;
    },

    skip: function(length) {
      this.position_ += length;
    },

    decodeUInt8: function() {
      var result = this.payload_.getUint8(this.position_, true);
      this.position_ += 1;
      return result;
    },

    decodeUInt16: function() {
      var result = this.payload_.getUint16(this.position_, true);
      this.position_ += 2;
      return result;
    },

    decodeUInt32: function() {
      var result = this.payload_.getUint32(this.position_, true);
      this.position_ += 4;
      return result;
    },

    decodeUInt64ToString: function() {
      // Javascript isn't able to manage 64-bit numeric values.
      var low = this.decodeUInt32();
      var high = this.decodeUInt32();
      var low_str = ('0000000' + low.toString(16)).substr(-8);
      var high_str = ('0000000' + high.toString(16)).substr(-8);
      var result = high_str + low_str;
      return result;
    },

    decodeInt8: function() {
      var result = this.payload_.getInt8(this.position_, true);
      this.position_ += 1;
      return result;
    },

    decodeInt16: function() {
      var result = this.payload_.getInt16(this.position_, true);
      this.position_ += 2;
      return result;
    },

    decodeInt32: function() {
      var result = this.payload_.getInt32(this.position_, true);
      this.position_ += 4;
      return result;
    },

    decodeInt64ToString: function() {
      // Javascript isn't able to manage 64-bit numeric values.
      // Fallback to unsigned 64-bit hexa value.
      return this.decodeUInt64ToString();
    },

    decodeUInteger: function(is64) {
      if (is64)
        return this.decodeUInt64ToString();
      return this.decodeUInt32();
    },

    decodeString: function() {
      var str = '';
      while (true) {
        var c = this.decodeUInt8();
        if (!c)
          return str;
        str = str + String.fromCharCode(c);
      }
    },

    decodeW16String: function() {
      var str = '';
      while (true) {
        var c = this.decodeUInt16();
        if (!c)
          return str;
        str = str + String.fromCharCode(c);
      }
    },

    decodeFixedW16String: function(length) {
      var old_position = this.position_;
      var str = '';
      for (var i = 0; i < length; i++) {
        var c = this.decodeUInt16();
        if (!c)
          break;
        str = str + String.fromCharCode(c);
      }

      // Move the position after the fixed buffer (i.e. wchar[length]).
      this.position_ = old_position + 2 * length;
      return str;
    },

    decodeBytes: function(length) {
      var bytes = [];
      for (var i = 0; i < length; ++i) {
        var c = this.decodeUInt8();
        bytes.push(c);
      }
      return bytes;
    },

    decodeSID: function(is64) {
      // Decode the TOKEN_USER structure.
      var pSid = this.decodeUInteger(is64);
      var attributes = this.decodeUInt32();

      // Skip padding.
      if (is64)
        this.decodeUInt32();

      // Decode the SID structure.
      var revision = this.decodeUInt8();
      var subAuthorityCount = this.decodeUInt8();
      this.decodeUInt16();
      this.decodeUInt32();

      if (revision != 1)
        throw 'Invalid SID revision: could not decode the SID structure.';

      var sid = this.decodeBytes(4 * subAuthorityCount);

      return {
        pSid: pSid,
        attributes: attributes,
        sid: sid
      };
    },

    decodeSystemTime: function() {
      // Decode the SystemTime structure.
      var wYear = this.decodeInt16();
      var wMonth = this.decodeInt16();
      var wDayOfWeek = this.decodeInt16();
      var wDay = this.decodeInt16();
      var wHour = this.decodeInt16();
      var wMinute = this.decodeInt16();
      var wSecond = this.decodeInt16();
      var wMilliseconds = this.decodeInt16();
      return {
        wYear: wYear,
        wMonth: wMonth,
        wDayOfWeek: wDayOfWeek,
        wDay: wDay,
        wHour: wHour,
        wMinute: wMinute,
        wSecond: wSecond,
        wMilliseconds: wMilliseconds
      };
    },

    decodeTimeZoneInformation: function() {
      // Decode the TimeZoneInformation structure.
      var bias = this.decodeUInt32();
      var standardName = this.decodeFixedW16String(32);
      var standardDate = this.decodeSystemTime();
      var standardBias = this.decodeUInt32();
      var daylightName = this.decodeFixedW16String(32);
      var daylightDate = this.decodeSystemTime();
      var daylightBias = this.decodeUInt32();
      return {
        bias: bias,
        standardName: standardName,
        standardDate: standardDate,
        standardBias: standardBias,
        daylightName: daylightName,
        daylightDate: daylightDate,
        daylightBias: daylightBias
      };
    }

  };

  /**
   * Imports Windows ETW kernel events into a specified model.
   * @constructor
   */
  function EtwImporter(model, events) {
    this.importPriority = 3;
    this.model_ = model;
    this.events_ = events;
    this.handlers_ = {};
    this.decoder_ = new Decoder();
    this.parsers_ = [];

    // A map of tids to their process pid. On Windows, the tid is global to
    // the system and doesn't need to belong to a process. As many events
    // only provide tid, this map allows to retrieve the parent process.
    this.tidsToPid_ = {};

    // Instantiate the parsers; this will register handlers for known events.
    var constructors = tracing.importer.etw.Parser.getSubtypeConstructors();
    for (var i = 0; i < constructors.length; ++i) {
      var parser = constructors[i];
      this.parsers_.push(new parser(this));
    }
  }

  /**
   * Guesses whether the provided events is from a Windows ETW trace.
   * The object must has a property named 'name' with the value 'ETW' and
   * a property 'content' with all the undecoded events.
   *
   * @return {boolean} True when events is a Windows ETW array.
   */
  EtwImporter.canImport = function(events) {
    if (!events.hasOwnProperty('name') ||
        !events.hasOwnProperty('content') ||
        events.name !== 'ETW') {
      return false;
    }

    return true;
  };

  EtwImporter.prototype = {
    __proto__: Importer.prototype,

    get model() {
      return this.model_;
    },

    createThreadIfNeeded: function(pid, tid) {
      this.tidsToPid_[tid] = pid;
    },

    getThreadFromWindowsTid: function(tid) {
      var pid = this.tidsToPid_[tid];
      if (pid == undefined)
        throw new Error('Process ID cannot be found.');
      return pid;
    },

    /**
     * Imports the data in this.events_ into this.model_.
     */
    importEvents: function(isSecondaryImport) {
      this.events_.content.forEach(this.parseEvent.bind(this));
    },

    parseEvent: function(event) {
      if (!event.hasOwnProperty('guid') ||
          !event.hasOwnProperty('op') ||
          !event.hasOwnProperty('ver') ||
          !event.hasOwnProperty('cpu') ||
          !event.hasOwnProperty('ts') ||
          !event.hasOwnProperty('payload')) {
        return false;
      }

      // Timestamp is 100-nanosecond intervals since midnight, January 1, 1601.
      // TODO(etienneb): substract the origin.
      var timestamp = (event.ts) / 10000.;
      var header = {
        guid: event.guid,
        opcode: event.op,
        version: event.ver,
        cpu: event.cpu,
        timestamp: timestamp,
        is64: event.ver
      };

      // Set the payload to decode.
      var decoder = this.decoder_;
      decoder.reset(event.payload);

      // Retrieve the handler to decode the payload.
      var handler = this.getEventHandler(header.guid, header.opcode);
      if (!handler)
        return false;

      if (!handler(header, decoder)) {
        this.model_.importWarning({
          type: 'parse_error',
          message: 'Malformed ' + header.guid + ' event (' + text + ')'
        });
        return false;
      }

      return true;
    },

    /**
     * Registers a windows ETW event handler used by parseEvent().
     */
    registerEventHandler: function(guid, opcode, handler) {
      var eventName = guid + '_' + opcode;
      this.handlers_[eventName] = handler;
    },

    /**
     * Retrieves a registered event handler.
     */
    getEventHandler: function(guid, opcode) {
      var eventName = guid + '_' + opcode;
      var handler = this.handlers_[eventName];
      return handler;
    }

  };

  // Register the EtwImporter to the Importer.
  tracing.TraceModel.registerImporter(EtwImporter);

  return {
    EtwImporter: EtwImporter
  };

});
<commit_msg>Avoid memory allocation with getEventHandler in EtwImporter.<commit_after>// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Imports JSON file with the raw payloads from a Windows event
 * trace into the Tracemodel. This format is outputted by Chrome running
 * on a Windows system.
 *
 * This importer assumes the events arrived as a JSON file and the payloads are
 * undecoded sequence of bytes in hex format string. The unit tests provide
 * examples of the trace format.
 *
 * The format of the system trace is
 *     {
 *       name: 'ETW',
 *       content: [ <events> ]
 *     }
  *
 * where the <events> are dictionary values with fields.
 *
 *     {
 *       guid: "1234-...",    // The unique GUID for the event.
 *       op: 12,              // The opcode of the event.
 *       ver: 1,              // The encoding version of the event.
 *       cpu: 0,              // The cpu id on which the event was captured.
 *       ts: 1092,            // The thread id on which the event was captured.
 *       payload: "aaaa"      // A base64 encoded string of the raw payload.
 *     }
 *
 * The payload is an undecoded version of the raw event sent by ETW.
 * This importer uses specific parsers to decode recognized events.
 * A parser need to register the recognized event by calling
 * registerEventHandler(guid, opcode, handler). The parser is responsible to
 * decode the payload and update the TraceModel.
 *
 * The payload formats are described there:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa364085(v=vs.85).aspx
 *
 */
'use strict';

tvcm.require('tvcm.base64');
tvcm.require('tracing.trace_model');
tvcm.require('tracing.importer.importer');
tvcm.require('tracing.importer.etw.process_parser');
tvcm.require('tracing.importer.etw.thread_parser');

tvcm.exportTo('tracing.importer', function() {

  var Importer = tracing.importer.Importer;

  /**
   * Represents the raw bytes payload decoder.
   * @constructor
   */
  function Decoder() {
    this.payload_ = new DataView(new ArrayBuffer(256));
  };

  Decoder.prototype = {
    __proto__: Object.prototype,

    reset: function(base64_payload) {
      var decoded_size = tvcm.Base64.getDecodedBufferLength(base64_payload);
      if (decoded_size > this.payload_.byteLength)
        this.payload_ = new DataView(new ArrayBuffer(decoded_size));

      tvcm.Base64.DecodeToTypedArray(base64_payload, this.payload_);
      this.position_ = 0;
    },

    skip: function(length) {
      this.position_ += length;
    },

    decodeUInt8: function() {
      var result = this.payload_.getUint8(this.position_, true);
      this.position_ += 1;
      return result;
    },

    decodeUInt16: function() {
      var result = this.payload_.getUint16(this.position_, true);
      this.position_ += 2;
      return result;
    },

    decodeUInt32: function() {
      var result = this.payload_.getUint32(this.position_, true);
      this.position_ += 4;
      return result;
    },

    decodeUInt64ToString: function() {
      // Javascript isn't able to manage 64-bit numeric values.
      var low = this.decodeUInt32();
      var high = this.decodeUInt32();
      var low_str = ('0000000' + low.toString(16)).substr(-8);
      var high_str = ('0000000' + high.toString(16)).substr(-8);
      var result = high_str + low_str;
      return result;
    },

    decodeInt8: function() {
      var result = this.payload_.getInt8(this.position_, true);
      this.position_ += 1;
      return result;
    },

    decodeInt16: function() {
      var result = this.payload_.getInt16(this.position_, true);
      this.position_ += 2;
      return result;
    },

    decodeInt32: function() {
      var result = this.payload_.getInt32(this.position_, true);
      this.position_ += 4;
      return result;
    },

    decodeInt64ToString: function() {
      // Javascript isn't able to manage 64-bit numeric values.
      // Fallback to unsigned 64-bit hexa value.
      return this.decodeUInt64ToString();
    },

    decodeUInteger: function(is64) {
      if (is64)
        return this.decodeUInt64ToString();
      return this.decodeUInt32();
    },

    decodeString: function() {
      var str = '';
      while (true) {
        var c = this.decodeUInt8();
        if (!c)
          return str;
        str = str + String.fromCharCode(c);
      }
    },

    decodeW16String: function() {
      var str = '';
      while (true) {
        var c = this.decodeUInt16();
        if (!c)
          return str;
        str = str + String.fromCharCode(c);
      }
    },

    decodeFixedW16String: function(length) {
      var old_position = this.position_;
      var str = '';
      for (var i = 0; i < length; i++) {
        var c = this.decodeUInt16();
        if (!c)
          break;
        str = str + String.fromCharCode(c);
      }

      // Move the position after the fixed buffer (i.e. wchar[length]).
      this.position_ = old_position + 2 * length;
      return str;
    },

    decodeBytes: function(length) {
      var bytes = [];
      for (var i = 0; i < length; ++i) {
        var c = this.decodeUInt8();
        bytes.push(c);
      }
      return bytes;
    },

    decodeSID: function(is64) {
      // Decode the TOKEN_USER structure.
      var pSid = this.decodeUInteger(is64);
      var attributes = this.decodeUInt32();

      // Skip padding.
      if (is64)
        this.decodeUInt32();

      // Decode the SID structure.
      var revision = this.decodeUInt8();
      var subAuthorityCount = this.decodeUInt8();
      this.decodeUInt16();
      this.decodeUInt32();

      if (revision != 1)
        throw 'Invalid SID revision: could not decode the SID structure.';

      var sid = this.decodeBytes(4 * subAuthorityCount);

      return {
        pSid: pSid,
        attributes: attributes,
        sid: sid
      };
    },

    decodeSystemTime: function() {
      // Decode the SystemTime structure.
      var wYear = this.decodeInt16();
      var wMonth = this.decodeInt16();
      var wDayOfWeek = this.decodeInt16();
      var wDay = this.decodeInt16();
      var wHour = this.decodeInt16();
      var wMinute = this.decodeInt16();
      var wSecond = this.decodeInt16();
      var wMilliseconds = this.decodeInt16();
      return {
        wYear: wYear,
        wMonth: wMonth,
        wDayOfWeek: wDayOfWeek,
        wDay: wDay,
        wHour: wHour,
        wMinute: wMinute,
        wSecond: wSecond,
        wMilliseconds: wMilliseconds
      };
    },

    decodeTimeZoneInformation: function() {
      // Decode the TimeZoneInformation structure.
      var bias = this.decodeUInt32();
      var standardName = this.decodeFixedW16String(32);
      var standardDate = this.decodeSystemTime();
      var standardBias = this.decodeUInt32();
      var daylightName = this.decodeFixedW16String(32);
      var daylightDate = this.decodeSystemTime();
      var daylightBias = this.decodeUInt32();
      return {
        bias: bias,
        standardName: standardName,
        standardDate: standardDate,
        standardBias: standardBias,
        daylightName: daylightName,
        daylightDate: daylightDate,
        daylightBias: daylightBias
      };
    }

  };

  /**
   * Imports Windows ETW kernel events into a specified model.
   * @constructor
   */
  function EtwImporter(model, events) {
    this.importPriority = 3;
    this.model_ = model;
    this.events_ = events;
    this.handlers_ = {};
    this.decoder_ = new Decoder();
    this.parsers_ = [];

    // A map of tids to their process pid. On Windows, the tid is global to
    // the system and doesn't need to belong to a process. As many events
    // only provide tid, this map allows to retrieve the parent process.
    this.tidsToPid_ = {};

    // Instantiate the parsers; this will register handlers for known events.
    var constructors = tracing.importer.etw.Parser.getSubtypeConstructors();
    for (var i = 0; i < constructors.length; ++i) {
      var parser = constructors[i];
      this.parsers_.push(new parser(this));
    }
  }

  /**
   * Guesses whether the provided events is from a Windows ETW trace.
   * The object must has a property named 'name' with the value 'ETW' and
   * a property 'content' with all the undecoded events.
   *
   * @return {boolean} True when events is a Windows ETW array.
   */
  EtwImporter.canImport = function(events) {
    if (!events.hasOwnProperty('name') ||
        !events.hasOwnProperty('content') ||
        events.name !== 'ETW') {
      return false;
    }

    return true;
  };

  EtwImporter.prototype = {
    __proto__: Importer.prototype,

    get model() {
      return this.model_;
    },

    createThreadIfNeeded: function(pid, tid) {
      this.tidsToPid_[tid] = pid;
    },

    getThreadFromWindowsTid: function(tid) {
      var pid = this.tidsToPid_[tid];
      if (pid == undefined)
        throw new Error('Process ID cannot be found.');
      return pid;
    },

    /**
     * Imports the data in this.events_ into this.model_.
     */
    importEvents: function(isSecondaryImport) {
      this.events_.content.forEach(this.parseEvent.bind(this));
    },

    parseEvent: function(event) {
      if (!event.hasOwnProperty('guid') ||
          !event.hasOwnProperty('op') ||
          !event.hasOwnProperty('ver') ||
          !event.hasOwnProperty('cpu') ||
          !event.hasOwnProperty('ts') ||
          !event.hasOwnProperty('payload')) {
        return false;
      }

      // Timestamp is 100-nanosecond intervals since midnight, January 1, 1601.
      // TODO(etienneb): substract the origin.
      var timestamp = (event.ts) / 10000.;
      var header = {
        guid: event.guid,
        opcode: event.op,
        version: event.ver,
        cpu: event.cpu,
        timestamp: timestamp,
        is64: event.ver
      };

      // Set the payload to decode.
      var decoder = this.decoder_;
      decoder.reset(event.payload);

      // Retrieve the handler to decode the payload.
      var handler = this.getEventHandler(header.guid, header.opcode);
      if (!handler)
        return false;

      if (!handler(header, decoder)) {
        this.model_.importWarning({
          type: 'parse_error',
          message: 'Malformed ' + header.guid + ' event (' + text + ')'
        });
        return false;
      }

      return true;
    },

    /**
     * Registers a windows ETW event handler used by parseEvent().
     */
    registerEventHandler: function(guid, opcode, handler) {
      if (this.handlers_[guid] == undefined)
        this.handlers_[guid] = [];
      this.handlers_[guid][opcode] = handler;
    },

    /**
     * Retrieves a registered event handler.
     */
    getEventHandler: function(guid, opcode) {
      if (this.handlers_[guid] == undefined)
        return undefined;
      return this.handlers_[guid][opcode];
    }

  };

  // Register the EtwImporter to the Importer.
  tracing.TraceModel.registerImporter(EtwImporter);

  return {
    EtwImporter: EtwImporter
  };

});
<|endoftext|>


<commit_before>var _ = require('lodash');
var moment = require('frozen-moment');
var periodTools = require('./periodTools');
var ressourcesMap = require('./ressources');
var ind = require('./individu');
var men = require('./menage');
var fam = require('./famille');
var foy = require('./foyerFiscal');
var common = require('./common');
var PRESTATIONS = require('../prestations');

var isNotValidValue = function(value) {
    return _.isNaN(value) || _.isUndefined(value) || value === null;
};

var getPeriods = function(dateDeValeur) {
    dateDeValeur = moment(dateDeValeur).freeze();
    return {
        moisCourant: {
            key: dateDeValeur.format('YYYY-MM')
        },
        moisPrecedent: {
            key: dateDeValeur.subtract(1, 'months').format('YYYY-MM')
        },
        moisMMoins2: {
            key: dateDeValeur.subtract(2, 'months').format('YYYY-MM')
        },
        moisMMoins3: {
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM')
        },
       anneeGlissante: {
            debut: dateDeValeur.subtract(1, 'years').subtract(1, 'months'),
            fin: dateDeValeur,
            key: 'year:' + dateDeValeur.subtract(1, 'years').format('YYYY-MM')
        },
        anneeFiscalReference: {
            key: dateDeValeur.subtract(2, 'years').year()
        },
        trimestreGlissant: {
            debut: dateDeValeur.subtract(3, 'months').subtract(1, 'months'),
            fin: dateDeValeur,
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM') + ':3'
        }
    };
};

var applyRessources = function(mesAidesEntity, openFiscaEntity, schema, dateDeValeur) {
    var periods = getPeriods(dateDeValeur);

    function isPeriodWithin(agregatKey, periode) {
        return periode.isAfter(periods[agregatKey].debut, 'month') && periode.isBefore(periods[agregatKey].fin, 'month');
    }

    var ressourcesByType = _.groupBy(mesAidesEntity.ressources, 'type');

    _.forEach(schema, function(definitions, openFiscaKey) {
        if (! _.isArray(definitions)) {
            definitions = [definitions];
        }

        _.forEach(definitions, function(definition) {

            var srcKey = definition.src || definition;
            var fn = definition.fn;

            if (ressourcesByType[srcKey]) {
                var result = {};
                var agr = {};

                _.forEach(ressourcesByType[srcKey], function(item) {
                    if (item.periode) {
                        // Période d'origine
                        if (!result[item.periode]) result[item.periode] = 0;
                        result[item.periode] += item.montant;

                        // Aggrégats
                        _.keys(periods).forEach(function (agregatKey) {
                            if (agregatKey != 'anneeFiscalReference' && isPeriodWithin(agregatKey, moment(item.periode, 'YYYY-MM'))) {
                                if (!(agregatKey in agr)) agr[agregatKey] = 0;
                                agr[agregatKey] += fn ? fn(item.montant) : item.montant;
                            }
                        });
                    }
                });

                _.forEach(result, function(montant, periode) {
                    // Applique la transformation
                    if (fn) montant = fn(montant);
                    result[periode] = montant;
                });

                // On prolonge la ressource perçue quand c'est possible
                if (result[periods.moisPrecedent.key] && !_.contains(mesAidesEntity.interruptedRessources, srcKey)) {
                    result[periods.moisCourant.key] = result[periods.moisPrecedent.key];
                }

                if (_.size(result) > 0) {
                    if (_.size(openFiscaEntity[openFiscaKey]) > 0) {
                        openFiscaEntity[openFiscaKey] = _.extend(openFiscaEntity[openFiscaKey],result);
                    } else {
                        openFiscaEntity[openFiscaKey] = result;
                    }
                }

                _.forEach(agr, function (value, agregatKey) {
                    openFiscaEntity[openFiscaKey][periods[agregatKey].key] = value;
                });

                // On suppose que les revenus pour l'année fiscale de référence sont les mêmes que pour les 12 derniers mois, si ces revenus n'ont pas encore
                // été renseignés
                var anneeGlissanteKey = periods.anneeGlissante.key,
                    anneeFiscaleRefKey = periods.anneeFiscalReference.key;
                if (! openFiscaEntity[openFiscaKey][anneeFiscaleRefKey]) {
                    openFiscaEntity[openFiscaKey][anneeFiscaleRefKey] = openFiscaEntity[openFiscaKey][anneeGlissanteKey];
                }
            }
        });
    });
};

var setNonInjectedPrestationsToZero = function(openFiscaEntity, dateDeValeur) {
    var periods = getPeriods(dateDeValeur);
    var concernedPeriods = ['moisPrecedent', 'moisMMoins2', 'moisMMoins3', 'anneeGlissante'];

    _.forEach(concernedPeriods, function(periodKey) {
        var period = periods[periodKey].key;
        var prestations = _.pick(PRESTATIONS, function(definition) {
            return definition.type == Number;
        });

        _.forEach(prestations, function(definition, prestationName) {
            if (! openFiscaEntity[prestationName]) {
                openFiscaEntity[prestationName] = {};
            }
            if (! openFiscaEntity[prestationName][period]) {
                openFiscaEntity[prestationName][period] = 0;
            }
        });

    });

};

var buildOpenFiscaEntity = function(mesAidesEntity, mappingSchema, situation) {
    var result = {};
    _.forEach(mappingSchema, function(definition, openFiscaKey) {
        var params = _.isString(definition) ? { src: definition } : definition;

        if (params.values) {
            result[openFiscaKey] = params.values[mesAidesEntity[params.src]];
        } else if (params.fn) {
            result[openFiscaKey] = params.src ? params.fn(mesAidesEntity[params.src], mesAidesEntity, situation) : params.fn(mesAidesEntity, situation);
        } else {
            result[openFiscaKey] = mesAidesEntity[params.src];
        }

        if (params.round && _.isNumber(result[openFiscaKey])) {
            result[openFiscaKey] = Math.round(result[openFiscaKey]);
        }

        // Supprime les null (mal supportés par OpenFisca)
        if (isNotValidValue(result[openFiscaKey])) {
            if ('default' in params) result[openFiscaKey] = params.default;
            else delete result[openFiscaKey];
        }
    });
    return result;
};

var mapIndividus = exports.mapIndividus = function (situation) {
    var individus = _.filter(situation.individus, function(individu) {
        return common.isIndividuValid(individu, situation);
    });

    return _.map(individus, function(individu) {
        if (individu.role === 'demandeur') {
            _.extend(individu, situation.patrimoine);

            var revenusLocatifs = situation.patrimoine.revenusLocatifs.map(function(revenu) {
                revenu.type = 'revenusLocatifs';
                return revenu;
            });

            var revenusDuCapital = situation.patrimoine.revenusDuCapital.map(function(revenu) {
                revenu.type = 'revenusDuCapital';
                return revenu;
            });

            individu.ressources = individu.ressources.concat(revenusLocatifs, revenusDuCapital);
        }
        var openFiscaIndividu = buildOpenFiscaEntity(individu, ind, situation);
        applyRessources(individu, openFiscaIndividu, ressourcesMap.individu, situation.dateDeValeur);
        return openFiscaIndividu;
    });
};

var mapFamille = function(situation) {
    var famille = buildOpenFiscaEntity(situation, fam, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, famille, ressourcesMap.famille, situation.dateDeValeur);
    setNonInjectedPrestationsToZero(famille, situation.dateDeValeur);

    return famille;
};

var mapFoyerFiscal = exports.mapFoyerFiscal = function(situation) {
    var foyerFiscal = buildOpenFiscaEntity(situation, foy, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, foyerFiscal, ressourcesMap.foyerFiscal, situation.dateDeValeur);

    return foyerFiscal;
};

exports.buildOpenFiscaRequest = function(situation, debug) {
        return {
        intermediate_variables: debug,
        labels: true,
        scenarios: [{
            test_case: {
                familles: [mapFamille(situation)],
                foyers_fiscaux: [mapFoyerFiscal(situation)],
                individus: mapIndividus(situation),
                menages: [buildOpenFiscaEntity(situation, men, situation)]
            },
            period: 'month:' + periodTools.toOpenFiscaFormat(situation.dateDeValeur)
        }],
        variables: _.keys(PRESTATIONS)
    };
};
<commit_msg>Rename schemas<commit_after>var _ = require('lodash');
var moment = require('frozen-moment');
var periodTools = require('./periodTools');
var ressourcesMap = require('./ressources');
var individuSchema = require('./individu');
var menageSchema = require('./menage');
var familleSchema = require('./famille');
var foyerSchema = require('./foyerFiscal');
var common = require('./common');
var PRESTATIONS = require('../prestations');

var isNotValidValue = function(value) {
    return _.isNaN(value) || _.isUndefined(value) || value === null;
};

var getPeriods = function(dateDeValeur) {
    dateDeValeur = moment(dateDeValeur).freeze();
    return {
        moisCourant: {
            key: dateDeValeur.format('YYYY-MM')
        },
        moisPrecedent: {
            key: dateDeValeur.subtract(1, 'months').format('YYYY-MM')
        },
        moisMMoins2: {
            key: dateDeValeur.subtract(2, 'months').format('YYYY-MM')
        },
        moisMMoins3: {
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM')
        },
       anneeGlissante: {
            debut: dateDeValeur.subtract(1, 'years').subtract(1, 'months'),
            fin: dateDeValeur,
            key: 'year:' + dateDeValeur.subtract(1, 'years').format('YYYY-MM')
        },
        anneeFiscalReference: {
            key: dateDeValeur.subtract(2, 'years').year()
        },
        trimestreGlissant: {
            debut: dateDeValeur.subtract(3, 'months').subtract(1, 'months'),
            fin: dateDeValeur,
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM') + ':3'
        }
    };
};

var applyRessources = function(mesAidesEntity, openFiscaEntity, schema, dateDeValeur) {
    var periods = getPeriods(dateDeValeur);

    function isPeriodWithin(agregatKey, periode) {
        return periode.isAfter(periods[agregatKey].debut, 'month') && periode.isBefore(periods[agregatKey].fin, 'month');
    }

    var ressourcesByType = _.groupBy(mesAidesEntity.ressources, 'type');

    _.forEach(schema, function(definitions, openFiscaKey) {
        if (! _.isArray(definitions)) {
            definitions = [definitions];
        }

        _.forEach(definitions, function(definition) {

            var srcKey = definition.src || definition;
            var fn = definition.fn;

            if (ressourcesByType[srcKey]) {
                var result = {};
                var agr = {};

                _.forEach(ressourcesByType[srcKey], function(item) {
                    if (item.periode) {
                        // Période d'origine
                        if (!result[item.periode]) result[item.periode] = 0;
                        result[item.periode] += item.montant;

                        // Aggrégats
                        _.keys(periods).forEach(function (agregatKey) {
                            if (agregatKey != 'anneeFiscalReference' && isPeriodWithin(agregatKey, moment(item.periode, 'YYYY-MM'))) {
                                if (!(agregatKey in agr)) agr[agregatKey] = 0;
                                agr[agregatKey] += fn ? fn(item.montant) : item.montant;
                            }
                        });
                    }
                });

                _.forEach(result, function(montant, periode) {
                    // Applique la transformation
                    if (fn) montant = fn(montant);
                    result[periode] = montant;
                });

                // On prolonge la ressource perçue quand c'est possible
                if (result[periods.moisPrecedent.key] && !_.contains(mesAidesEntity.interruptedRessources, srcKey)) {
                    result[periods.moisCourant.key] = result[periods.moisPrecedent.key];
                }

                if (_.size(result) > 0) {
                    if (_.size(openFiscaEntity[openFiscaKey]) > 0) {
                        openFiscaEntity[openFiscaKey] = _.extend(openFiscaEntity[openFiscaKey],result);
                    } else {
                        openFiscaEntity[openFiscaKey] = result;
                    }
                }

                _.forEach(agr, function (value, agregatKey) {
                    openFiscaEntity[openFiscaKey][periods[agregatKey].key] = value;
                });

                // On suppose que les revenus pour l'année fiscale de référence sont les mêmes que pour les 12 derniers mois, si ces revenus n'ont pas encore
                // été renseignés
                var anneeGlissanteKey = periods.anneeGlissante.key,
                    anneeFiscaleRefKey = periods.anneeFiscalReference.key;
                if (! openFiscaEntity[openFiscaKey][anneeFiscaleRefKey]) {
                    openFiscaEntity[openFiscaKey][anneeFiscaleRefKey] = openFiscaEntity[openFiscaKey][anneeGlissanteKey];
                }
            }
        });
    });
};

var setNonInjectedPrestationsToZero = function(openFiscaEntity, dateDeValeur) {
    var periods = getPeriods(dateDeValeur);
    var concernedPeriods = ['moisPrecedent', 'moisMMoins2', 'moisMMoins3', 'anneeGlissante'];

    _.forEach(concernedPeriods, function(periodKey) {
        var period = periods[periodKey].key;
        var prestations = _.pick(PRESTATIONS, function(definition) {
            return definition.type == Number;
        });

        _.forEach(prestations, function(definition, prestationName) {
            if (! openFiscaEntity[prestationName]) {
                openFiscaEntity[prestationName] = {};
            }
            if (! openFiscaEntity[prestationName][period]) {
                openFiscaEntity[prestationName][period] = 0;
            }
        });

    });

};

var buildOpenFiscaEntity = function(mesAidesEntity, mappingSchema, situation) {
    var result = {};
    _.forEach(mappingSchema, function(definition, openFiscaKey) {
        var params = _.isString(definition) ? { src: definition } : definition;

        if (params.values) {
            result[openFiscaKey] = params.values[mesAidesEntity[params.src]];
        } else if (params.fn) {
            result[openFiscaKey] = params.src ? params.fn(mesAidesEntity[params.src], mesAidesEntity, situation) : params.fn(mesAidesEntity, situation);
        } else {
            result[openFiscaKey] = mesAidesEntity[params.src];
        }

        if (params.round && _.isNumber(result[openFiscaKey])) {
            result[openFiscaKey] = Math.round(result[openFiscaKey]);
        }

        // Supprime les null (mal supportés par OpenFisca)
        if (isNotValidValue(result[openFiscaKey])) {
            if ('default' in params) result[openFiscaKey] = params.default;
            else delete result[openFiscaKey];
        }
    });
    return result;
};

var mapIndividus = exports.mapIndividus = function (situation) {
    var individus = _.filter(situation.individus, function(individu) {
        return common.isIndividuValid(individu, situation);
    });

    return _.map(individus, function(individu) {
        if (individu.role === 'demandeur') {
            _.extend(individu, situation.patrimoine);

            var revenusLocatifs = situation.patrimoine.revenusLocatifs.map(function(revenu) {
                revenu.type = 'revenusLocatifs';
                return revenu;
            });

            var revenusDuCapital = situation.patrimoine.revenusDuCapital.map(function(revenu) {
                revenu.type = 'revenusDuCapital';
                return revenu;
            });

            individu.ressources = individu.ressources.concat(revenusLocatifs, revenusDuCapital);
        }
        var openFiscaIndividu = buildOpenFiscaEntity(individu, individuSchema, situation);
        applyRessources(individu, openFiscaIndividu, ressourcesMap.individu, situation.dateDeValeur);
        return openFiscaIndividu;
    });
};

var mapFamille = function(situation) {
    var famille = buildOpenFiscaEntity(situation, familleSchema, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, famille, ressourcesMap.famille, situation.dateDeValeur);
    setNonInjectedPrestationsToZero(famille, situation.dateDeValeur);

    return famille;
};

var mapFoyerFiscal = exports.mapFoyerFiscal = function(situation) {
    var foyerFiscal = buildOpenFiscaEntity(situation, foyerSchema, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, foyerFiscal, ressourcesMap.foyerFiscal, situation.dateDeValeur);

    return foyerFiscal;
};

exports.buildOpenFiscaRequest = function(situation, debug) {
        return {
        intermediate_variables: debug,
        labels: true,
        scenarios: [{
            test_case: {
                familles: [mapFamille(situation)],
                foyers_fiscaux: [mapFoyerFiscal(situation)],
                individus: mapIndividus(situation),
                menages: [buildOpenFiscaEntity(situation, menageSchema, situation)]
            },
            period: 'month:' + periodTools.toOpenFiscaFormat(situation.dateDeValeur)
        }],
        variables: _.keys(PRESTATIONS)
    };
};
<|endoftext|>


<commit_before>import React, {
  Component,
  PropTypes
} from 'react';
import {
  TouchableOpacity,
  View,
  Text
} from 'react-native';

/**
 * @author 田尘殇Sean(sean.snow@live.com)
 * @date 16/5/5
 */
class Button extends Component {

  static propTypes = {
    activeOpacity: PropTypes.number,
    children: PropTypes.any,
    disabled: PropTypes.bool,
    renderContent: PropTypes.func
  };

  static defaultProps = {
    activeOpacity: .6,
    disabled: false
  };

  renderContent(children, renderContent) {
    if (renderContent) {
      return renderContent();
    }

    if (typeof children === 'string' ||
      (Array.isArray(children) && typeof children[0] === 'string')) {
      return (
        <Text>{Array.isArray(children) ? children[0] : children}</Text>
      );
    }
    return <Text />;
  }

  render() {
    let {
      disabled,
      children,
      renderContent,
      ...other
    } = this.props;

    let Comp = disabled ? View : TouchableOpacity;

    return (
      <Comp {...other}>
        {this.renderContent(children, renderContent)}
      </Comp>
    );
  }

}

export default Button;
<commit_msg>去掉默认值设置<commit_after>import React, {
  Component,
  PropTypes
} from 'react';
import {
  TouchableOpacity,
  View,
  Text
} from 'react-native';

/**
 * @author 田尘殇Sean(sean.snow@live.com)
 * @date 16/5/5
 */
class Button extends Component {

  static propTypes = {
    activeOpacity: PropTypes.number,
    children: PropTypes.any,
    disabled: PropTypes.bool,
    renderContent: PropTypes.func
  };

  static defaultProps = {
    disabled: false
  };

  renderContent(children, renderContent) {
    if (renderContent) {
      return renderContent();
    }

    if (typeof children === 'string' ||
      (Array.isArray(children) && typeof children[0] === 'string')) {
      return (
        <Text>{Array.isArray(children) ? children[0] : children}</Text>
      );
    }
    return <Text />;
  }

  render() {
    let {
      disabled,
      children,
      renderContent,
      ...other
    } = this.props;

    let Comp = disabled ? View : TouchableOpacity;

    return (
      <Comp {...other}>
        {this.renderContent(children, renderContent)}
      </Comp>
    );
  }

}

export default Button;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import coalesceNonElementChildren from './coalesceNonElementChildren';

const systemButtonOpacity = 0.2;

export default class Button extends Component {
  static propTypes = {
    ...TouchableOpacity.propTypes,
    containerStyle: View.propTypes.style,
    disabled: PropTypes.bool,
    style: Text.propTypes.style,
    styleDisabled: Text.propTypes.style,
  };

  render() {
    let touchableProps = {
      activeOpacity: this._computeActiveOpacity(),
    };
    if (!this.props.disabled) {
      touchableProps.onPress = this.props.onPress;
      touchableProps.onPressIn = this.props.onPressIn;
      touchableProps.onPressOut = this.props.onPressOut;
      touchableProps.onLongPress = this.props.onLongPress;
    }

    return (
      <TouchableOpacity
        {...touchableProps}
        testID={this.props.testID}
        style={this.props.containerStyle}
        accessibilityTraits="button"
        accessibilityComponentType="button"
      >
        {this._renderGroupedChildren()}
      </TouchableOpacity>
    );
  }

  _renderGroupedChildren() {
    let { disabled } = this.props;
    let style = [
      styles.text,
      disabled ? styles.disabledText : null,
      this.props.style,
      disabled ? this.props.styleDisabled : null,
    ];

    let children = coalesceNonElementChildren(this.props.children, (children, index) => {
      return (
        <Text key={index} style={style}>
          {children}
        </Text>
      );
    });

    switch (children.length) {
      case 0:
        return null;
      case 1:
        return children[0];
      default:
        return <View style={styles.group}>{children}</View>;
    }
  }

  _computeActiveOpacity() {
    if (this.props.disabled) {
      return 1;
    }
    return this.props.activeOpacity != null ?
      this.props.activeOpacity :
      systemButtonOpacity;
  }
};

const styles = StyleSheet.create({
  text: {
    color: '#007aff',
    fontFamily: '.HelveticaNeueInterface-MediumP4',
    fontSize: 17,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  disabledText: {
    color: '#dcdcdc',
  },
  group: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});
<commit_msg>Use public font family for button text (#50)<commit_after>import React, { Component, PropTypes } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import coalesceNonElementChildren from './coalesceNonElementChildren';

const systemButtonOpacity = 0.2;

export default class Button extends Component {
  static propTypes = {
    ...TouchableOpacity.propTypes,
    containerStyle: View.propTypes.style,
    disabled: PropTypes.bool,
    style: Text.propTypes.style,
    styleDisabled: Text.propTypes.style,
  };

  render() {
    let touchableProps = {
      activeOpacity: this._computeActiveOpacity(),
    };
    if (!this.props.disabled) {
      touchableProps.onPress = this.props.onPress;
      touchableProps.onPressIn = this.props.onPressIn;
      touchableProps.onPressOut = this.props.onPressOut;
      touchableProps.onLongPress = this.props.onLongPress;
    }

    return (
      <TouchableOpacity
        {...touchableProps}
        testID={this.props.testID}
        style={this.props.containerStyle}
        accessibilityTraits="button"
        accessibilityComponentType="button"
      >
        {this._renderGroupedChildren()}
      </TouchableOpacity>
    );
  }

  _renderGroupedChildren() {
    let { disabled } = this.props;
    let style = [
      styles.text,
      disabled ? styles.disabledText : null,
      this.props.style,
      disabled ? this.props.styleDisabled : null,
    ];

    let children = coalesceNonElementChildren(this.props.children, (children, index) => {
      return (
        <Text key={index} style={style}>
          {children}
        </Text>
      );
    });

    switch (children.length) {
      case 0:
        return null;
      case 1:
        return children[0];
      default:
        return <View style={styles.group}>{children}</View>;
    }
  }

  _computeActiveOpacity() {
    if (this.props.disabled) {
      return 1;
    }
    return this.props.activeOpacity != null ?
      this.props.activeOpacity :
      systemButtonOpacity;
  }
};

const styles = StyleSheet.create({
  text: {
    color: '#007aff',
    fontFamily: 'HelveticaNeue-Medium',
    fontSize: 17,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  disabledText: {
    color: '#dcdcdc',
  },
  group: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});
<|endoftext|>


<commit_before>/**
 * @param {Object} loaderConfig Loader configuration from Webpack config
 * @returns {string}
 */
function stringifyLoaderConfig(loaderConfig) {
  var stringified = null;

  if (Array.isArray(loaderConfig.loaders)) {
    stringified = loaderConfig.loaders.join('!');
  }
  else {
    stringified = loaderConfig.loader;
    if (loaderConfig.query) {
      var queryStr = (typeof loaderConfig.query === 'string')
        ? loaderConfig.query
        : JSON.stringify(loaderConfig.query);

      stringified += '?' + queryStr;
    }
  }

  return stringified;
}
module.exports = stringifyLoaderConfig;<commit_msg>Change precedence of loader config stringifier<commit_after>/**
 * @param {Object} loaderConfig Loader configuration from Webpack config
 * @returns {string}
 */
function stringifyLoaderConfig(loaderConfig) {
  var stringified = null;

  if (loaderConfig.hasOwnProperty('loader')) {
    stringified = loaderConfig.loader;
    if (loaderConfig.query) {
      var queryStr = (typeof loaderConfig.query === 'string')
        ? loaderConfig.query
        : JSON.stringify(loaderConfig.query);

      stringified += '?' + queryStr;
    }
  }
  else if (Array.isArray(loaderConfig.loaders)) {
    stringified = loaderConfig.loaders.join('!');
  }
  else
    throw new Error('Loader config must contain at least `loader` or `loaders` properties (in order of precedence)');

  return stringified;
}
module.exports = stringifyLoaderConfig;
<|endoftext|>


<commit_before>var carto = require('carto');
var debug = require('debug')('windshaft:renderer:torque_factory');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var torque_reference = require('torque.js').cartocss_reference;
var format = require('../../utils/format');

var RendererParams = require('../renderer_params');

var sql = require('./sql');
var Renderer = require('./renderer');
var PSQLAdaptor = require('./psql_adaptor');
var PngRenderer = require('./png_renderer');
var BaseAdaptor = require('../base_adaptor');

var SubstitutionTokens = require('./substitution_tokens');

/// API: initializes the renderer, it should be called once
//
/// @param options initialization options.
///     - sqlClass: class used to access postgres, by default is PSQL
///         the class should provide the following interface
///          - constructor(params) where params should contain:
///            host, port, database, user, password.
///            the class is always constructed with dbParams passed to
///            getRender as-is
///          - query(sql, callback(err, data), readonly)
///            gets an SQL query and return a javascript object with
///            the same structure of a JSON format response from
///            CartoDB-SQL-API, for reference see
///            http://github.com/CartoDB/CartoDB-SQL-API/blob/1.8.2/doc/API.md#json
///            The 'readonly' parameter (false by default) requests
///            that running the query should not allowed to change the database.
///     - dbPoolParams: database connection pool params
///          - size: maximum number of resources to create at any given time
///          - idleTimeout: max milliseconds a resource can go unused before it should be destroyed
///          - reapInterval: frequency to check for idle resources
///
function TorqueFactory(options) {
    // jshint newcap:false
    this.options = options || {};
    _.defaults(this.options, {
        sqlClass: PSQLAdaptor(PSQL, options.dbPoolParams)
    });
    if (this.options.sqlClass) {
        this.sqlClass = this.options.sqlClass;
    }
}

module.exports = TorqueFactory;

var formatToRenderer = {
    'torque.json': Renderer,
    'torque.bin': Renderer,
    'json.torque': Renderer,
    'bin.torque': Renderer,
    'png': PngRenderer,
    'torque.png': PngRenderer
};

TorqueFactory.prototype = {
    /// API: renderer name, use for information purposes
    name: 'torque',

    /// API: tile formats this module is able to render
    // TODO: deprecate 'json.torque' and 'bin.torque' with 1.18.0
    supported_formats: Object.keys(formatToRenderer),

    getName: function() {
        return this.name;
    },

    supportsFormat: function(format) {
        return !!formatToRenderer[format];
    },

    getAdaptor: function(renderer, format, onTileErrorStrategy) {
        return new BaseAdaptor(renderer, format, onTileErrorStrategy);
    },

    getRenderer: function(mapConfig, format, options, callback) {
        var dbParams = RendererParams.dbParamsFromReqParams(options.params);
        var layer = options.layer;

        if (_.isUndefined(layer)) {
            return callback(new Error("torque renderer only supports a single layer"));
        }
        if (!formatToRenderer[format]) {
            callback(new Error("format not supported: " + format));
            return;
        }
        var layerObj = mapConfig.getLayer(layer);
        if ( ! layerObj ) {
            callback(new Error("layer index is greater than number of layers"));
            return;
        }
        if ( layerObj.type !== 'torque' ) {
            callback(new Error("layer " + layer + " is not a torque layer"));
            return;
        }

        _.extend(dbParams, mapConfig.getLayerDatasource(layer));

        var pgSQL = sql(dbParams, this.sqlClass);
        fetchMapConfigAttributes(pgSQL, mapConfig, dbParams, layer, function(err, params){
            if (err) {
                callback(err);
                return;
            }
            var RendererClass = formatToRenderer[format];
            callback(null, new RendererClass(layerObj, pgSQL, params, layer));
        });
    }

};

//
// given cartocss return javascript properties
//
// @param required optional array of required properties
//
// @throw Error if required properties are not found
//
function attrsFromCartoCSS(cartocss, required) {
  var attrs_keys = {
    '-torque-frame-count': 'steps',
    '-torque-resolution': 'resolution',
    '-torque-animation-duration': 'animationDuration',
    '-torque-aggregation-function': 'countby',
    '-torque-time-attribute': 'column',
    '-torque-data-aggregation': 'data_aggregation'
  };
  carto.tree.Reference.setData(torque_reference.version.latest);
  var env = {
      benchmark: false,
      validation_data: false,
      effects: [],
      errors: [],
      error: function(e) {
        this.errors.push(e);
      }
  };
  var symbolizers = torque_reference.version.latest.layer;
  var root = (carto.Parser(env)).parse(cartocss);
  var definitions = root.toList(env);
  var rules = getMapProperties(definitions, env);
  var attrs = {};
  for (var k in attrs_keys) {
    if (rules[k]) {
      attrs[attrs_keys[k]] = rules[k].value.toString();
      var element = rules[k].value.value[0];
      var type = symbolizers[k].type;
      if(!checkValidType(element, type)){
          throw new Error("Unexpected type for property '" + k + "', expected " + type);
      }
    } else if ( required && required.indexOf(attrs_keys[k]) !== -1 ) {
        throw new Error("Missing required property '" + k + "' in torque layer CartoCSS");
    }

  }
  return attrs;
}

function checkValidType(e, type){
    if (["number", "float"].indexOf(type) > -1) {
      return typeof e.value === "number";
    }
    else if (type === "string"){
      return e.value !== "undefined" && typeof e.value === "string";
    }
    else if (type.constructor === Array){
      return type.indexOf(e.value) > -1 || e.value === "linear";
    }
    else if (type === "color"){
      return checkValidColor(e);
    }
    return true;
}

function checkValidColor(e){
  var expectedArguments = { rgb: 3, hsl: 3, rgba: 4, hsla: 4};
  return typeof e.rgb !== "undefined" || expectedArguments[e.name] === e.args;
}

//
// get rules from Map definition
// stores errors in env.error
//
function getMapProperties(definitions, env) {
  var rules = {};
  definitions.filter(function(r) {
      return r.elements.join('') === 'Map';
  }).forEach(function(r) {
      for (var i = 0; i < r.rules.length; i++) {
          var key = r.rules[i].name;
          rules[key] = r.rules[i].ev(env);
      }
  });
  return rules;
}

//
// check layer and raise an exception is some error is found
//
// @throw Error if missing or malformed CartoCSS
//
function _checkLayerAttributes(layerConfig) {
  var cartoCSS = layerConfig.options.cartocss;
  if (!cartoCSS) {
    throw new Error("cartocss can't be undefined");
  }
  var checks = ['steps', 'resolution', 'column', 'countby'];
  return attrsFromCartoCSS(cartoCSS, checks);
}

// returns an array of errors if the mapconfig is not supported or contains errors
// errors is empty is the configuration is ok
// dbParams: host, dbname, user and pass
// layer: optional, if is specified only a layer is checked
function fetchMapConfigAttributes(sql, mapConfig, dbParams, layer_id, callback) {

  // only one layer supported
  var layer = mapConfig.getLayer(layer_id);

  if (!layer) {
    callback(new Error("mapconfig does not contain any torque layer"));
    return;
  }

  // check attrs
  var attrs;
  try {
      attrs = _checkLayerAttributes(layer);
  }  catch (e) {
      return callback(e);
  }

  // fetch layer attributes to check the query and so on is ok
  var layer_sql = layer.options.sql;
  fetchLayerAttributes(sql, layer_sql, attrs, function(err, layerAttrs) {
    if (err) {
      callback(err);
      return;
    }
    callback(null, _.extend(attrs, layerAttrs));
  });
}

function fetchLayerAttributes(sql, layer_sql, attrs, callback) {
  var is_time;

  layer_sql = SubstitutionTokens.replace(layer_sql, {
      bbox: 'ST_MakeEnvelope(0,0,0,0)',
      scale_denominator: '0',
      pixel_width: '1',
      pixel_height: '1'
  });

  step(

  //
  // first step, fetch the schema to know if torque colum is time column
  //
  function fetchSqlSchema() {
    var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: layer_sql });
    sql(query, this);
  },

  //
  // second step, get time bounds to calculate step
  //
  function fetchProps(err, data) {
    if (err) {
        throw err;
    }
    if ( ! data ) {
        debug("ERROR: layer query '" + layer_sql + "' returned no data");
        throw new Error("Layer query returned no data");
    }
    if ( ! data.fields.hasOwnProperty(attrs.column) ) {
        debug("ERROR: layer query " + layer_sql + " does not include time-attribute column '" + attrs.column + "'");
        throw new Error("Layer query did not return the requested time-attribute column '" + attrs.column + "'");
    }
    is_time = data.fields[attrs.column].type === 'date';
    var column_conv = attrs.column;
    var max_tmpl, min_tmpl;
    if (is_time){
      max_tmpl = "date_part('epoch', max({column}))";
      min_tmpl = "date_part('epoch', min({column}))";
      column_conv = format("date_part('epoch', {column})", attrs);
    } else {
      max_tmpl = "max({column})";
      min_tmpl = "min({column})";
    }

    var max_col = format(max_tmpl, { column: attrs.column });
    var min_col = format(min_tmpl, { column: attrs.column });
    var sql_stats = " SELECT " +
        "count(*) as num_steps, " +
        "{max_col} max_date, " +
        "{min_col} min_date FROM  ({sql}) __torque_wrap_sql ";

    var query = format(sql_stats, {
      max_col: max_col,
      min_col: min_col,
      column: column_conv,
      sql: layer_sql
    });
    sql(query, this);
  },

  //
  // prepare metadata needed to render the tiles
  //
  function generateMetadata(err, data) {
    if(err) {
      if ( err.message ) {
          err.message = 'TorqueRenderer: ' + err.message;
      }
      callback(err);
      return;
    }
    data = data.rows[0];
    var step = (data.max_date - data.min_date + 1)/Math.min(attrs.steps, data.num_steps>>0);
    step = Math.abs(step) === Infinity ? 0 : step;
    callback(null, {
      start: data.min_date,
      end: data.max_date,
      step: step || 1,
      data_steps: data.num_steps >> 0,
      is_time: is_time
    });
  });
}
<commit_msg>return on callback<commit_after>var carto = require('carto');
var debug = require('debug')('windshaft:renderer:torque_factory');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var torque_reference = require('torque.js').cartocss_reference;
var format = require('../../utils/format');

var RendererParams = require('../renderer_params');

var sql = require('./sql');
var Renderer = require('./renderer');
var PSQLAdaptor = require('./psql_adaptor');
var PngRenderer = require('./png_renderer');
var BaseAdaptor = require('../base_adaptor');

var SubstitutionTokens = require('./substitution_tokens');

/// API: initializes the renderer, it should be called once
//
/// @param options initialization options.
///     - sqlClass: class used to access postgres, by default is PSQL
///         the class should provide the following interface
///          - constructor(params) where params should contain:
///            host, port, database, user, password.
///            the class is always constructed with dbParams passed to
///            getRender as-is
///          - query(sql, callback(err, data), readonly)
///            gets an SQL query and return a javascript object with
///            the same structure of a JSON format response from
///            CartoDB-SQL-API, for reference see
///            http://github.com/CartoDB/CartoDB-SQL-API/blob/1.8.2/doc/API.md#json
///            The 'readonly' parameter (false by default) requests
///            that running the query should not allowed to change the database.
///     - dbPoolParams: database connection pool params
///          - size: maximum number of resources to create at any given time
///          - idleTimeout: max milliseconds a resource can go unused before it should be destroyed
///          - reapInterval: frequency to check for idle resources
///
function TorqueFactory(options) {
    // jshint newcap:false
    this.options = options || {};
    _.defaults(this.options, {
        sqlClass: PSQLAdaptor(PSQL, options.dbPoolParams)
    });
    if (this.options.sqlClass) {
        this.sqlClass = this.options.sqlClass;
    }
}

module.exports = TorqueFactory;

var formatToRenderer = {
    'torque.json': Renderer,
    'torque.bin': Renderer,
    'json.torque': Renderer,
    'bin.torque': Renderer,
    'png': PngRenderer,
    'torque.png': PngRenderer
};

TorqueFactory.prototype = {
    /// API: renderer name, use for information purposes
    name: 'torque',

    /// API: tile formats this module is able to render
    // TODO: deprecate 'json.torque' and 'bin.torque' with 1.18.0
    supported_formats: Object.keys(formatToRenderer),

    getName: function() {
        return this.name;
    },

    supportsFormat: function(format) {
        return !!formatToRenderer[format];
    },

    getAdaptor: function(renderer, format, onTileErrorStrategy) {
        return new BaseAdaptor(renderer, format, onTileErrorStrategy);
    },

    getRenderer: function(mapConfig, format, options, callback) {
        var dbParams = RendererParams.dbParamsFromReqParams(options.params);
        var layer = options.layer;

        if (_.isUndefined(layer)) {
            return callback(new Error("torque renderer only supports a single layer"));
        }
        if (!formatToRenderer[format]) {
            return callback(new Error("format not supported: " + format));
        }
        var layerObj = mapConfig.getLayer(layer);
        if ( ! layerObj ) {
            return callback(new Error("layer index is greater than number of layers"));
        }
        if ( layerObj.type !== 'torque' ) {
            return callback(new Error("layer " + layer + " is not a torque layer"));
        }

        _.extend(dbParams, mapConfig.getLayerDatasource(layer));

        var pgSQL = sql(dbParams, this.sqlClass);
        fetchMapConfigAttributes(pgSQL, mapConfig, dbParams, layer, function(err, params){
            if (err) {
                callback(err);
                return;
            }
            var RendererClass = formatToRenderer[format];
            callback(null, new RendererClass(layerObj, pgSQL, params, layer));
        });
    }

};

//
// given cartocss return javascript properties
//
// @param required optional array of required properties
//
// @throw Error if required properties are not found
//
function attrsFromCartoCSS(cartocss, required) {
  var attrs_keys = {
    '-torque-frame-count': 'steps',
    '-torque-resolution': 'resolution',
    '-torque-animation-duration': 'animationDuration',
    '-torque-aggregation-function': 'countby',
    '-torque-time-attribute': 'column',
    '-torque-data-aggregation': 'data_aggregation'
  };
  carto.tree.Reference.setData(torque_reference.version.latest);
  var env = {
      benchmark: false,
      validation_data: false,
      effects: [],
      errors: [],
      error: function(e) {
        this.errors.push(e);
      }
  };
  var symbolizers = torque_reference.version.latest.layer;
  var root = (carto.Parser(env)).parse(cartocss);
  var definitions = root.toList(env);
  var rules = getMapProperties(definitions, env);
  var attrs = {};
  for (var k in attrs_keys) {
    if (rules[k]) {
      attrs[attrs_keys[k]] = rules[k].value.toString();
      var element = rules[k].value.value[0];
      var type = symbolizers[k].type;
      if(!checkValidType(element, type)){
          throw new Error("Unexpected type for property '" + k + "', expected " + type);
      }
    } else if ( required && required.indexOf(attrs_keys[k]) !== -1 ) {
        throw new Error("Missing required property '" + k + "' in torque layer CartoCSS");
    }

  }
  return attrs;
}

function checkValidType(e, type){
    if (["number", "float"].indexOf(type) > -1) {
      return typeof e.value === "number";
    }
    else if (type === "string"){
      return e.value !== "undefined" && typeof e.value === "string";
    }
    else if (type.constructor === Array){
      return type.indexOf(e.value) > -1 || e.value === "linear";
    }
    else if (type === "color"){
      return checkValidColor(e);
    }
    return true;
}

function checkValidColor(e){
  var expectedArguments = { rgb: 3, hsl: 3, rgba: 4, hsla: 4};
  return typeof e.rgb !== "undefined" || expectedArguments[e.name] === e.args;
}

//
// get rules from Map definition
// stores errors in env.error
//
function getMapProperties(definitions, env) {
  var rules = {};
  definitions.filter(function(r) {
      return r.elements.join('') === 'Map';
  }).forEach(function(r) {
      for (var i = 0; i < r.rules.length; i++) {
          var key = r.rules[i].name;
          rules[key] = r.rules[i].ev(env);
      }
  });
  return rules;
}

//
// check layer and raise an exception is some error is found
//
// @throw Error if missing or malformed CartoCSS
//
function _checkLayerAttributes(layerConfig) {
  var cartoCSS = layerConfig.options.cartocss;
  if (!cartoCSS) {
    throw new Error("cartocss can't be undefined");
  }
  var checks = ['steps', 'resolution', 'column', 'countby'];
  return attrsFromCartoCSS(cartoCSS, checks);
}

// returns an array of errors if the mapconfig is not supported or contains errors
// errors is empty is the configuration is ok
// dbParams: host, dbname, user and pass
// layer: optional, if is specified only a layer is checked
function fetchMapConfigAttributes(sql, mapConfig, dbParams, layer_id, callback) {

  // only one layer supported
  var layer = mapConfig.getLayer(layer_id);

  if (!layer) {
    return callback(new Error("mapconfig does not contain any torque layer"));
  }

  // check attrs
  var attrs;
  try {
      attrs = _checkLayerAttributes(layer);
  }  catch (e) {
      return callback(e);
  }

  // fetch layer attributes to check the query and so on is ok
  var layer_sql = layer.options.sql;
  fetchLayerAttributes(sql, layer_sql, attrs, function(err, layerAttrs) {
    if (err) {
      return callback(err);
    }
    callback(null, _.extend(attrs, layerAttrs));
  });
}

function fetchLayerAttributes(sql, layer_sql, attrs, callback) {
  var is_time;

  layer_sql = SubstitutionTokens.replace(layer_sql, {
      bbox: 'ST_MakeEnvelope(0,0,0,0)',
      scale_denominator: '0',
      pixel_width: '1',
      pixel_height: '1'
  });

  step(

  //
  // first step, fetch the schema to know if torque colum is time column
  //
  function fetchSqlSchema() {
    var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: layer_sql });
    sql(query, this);
  },

  //
  // second step, get time bounds to calculate step
  //
  function fetchProps(err, data) {
    if (err) {
        throw err;
    }
    if ( ! data ) {
        debug("ERROR: layer query '" + layer_sql + "' returned no data");
        throw new Error("Layer query returned no data");
    }
    if ( ! data.fields.hasOwnProperty(attrs.column) ) {
        debug("ERROR: layer query " + layer_sql + " does not include time-attribute column '" + attrs.column + "'");
        throw new Error("Layer query did not return the requested time-attribute column '" + attrs.column + "'");
    }
    is_time = data.fields[attrs.column].type === 'date';
    var column_conv = attrs.column;
    var max_tmpl, min_tmpl;
    if (is_time){
      max_tmpl = "date_part('epoch', max({column}))";
      min_tmpl = "date_part('epoch', min({column}))";
      column_conv = format("date_part('epoch', {column})", attrs);
    } else {
      max_tmpl = "max({column})";
      min_tmpl = "min({column})";
    }

    var max_col = format(max_tmpl, { column: attrs.column });
    var min_col = format(min_tmpl, { column: attrs.column });
    var sql_stats = " SELECT " +
        "count(*) as num_steps, " +
        "{max_col} max_date, " +
        "{min_col} min_date FROM  ({sql}) __torque_wrap_sql ";

    var query = format(sql_stats, {
      max_col: max_col,
      min_col: min_col,
      column: column_conv,
      sql: layer_sql
    });
    sql(query, this);
  },

  //
  // prepare metadata needed to render the tiles
  //
  function generateMetadata(err, data) {
    if(err) {
      if ( err.message ) {
          err.message = 'TorqueRenderer: ' + err.message;
      }
      callback(err);
      return;
    }
    data = data.rows[0];
    var step = (data.max_date - data.min_date + 1)/Math.min(attrs.steps, data.num_steps>>0);
    step = Math.abs(step) === Infinity ? 0 : step;
    callback(null, {
      start: data.min_date,
      end: data.max_date,
      step: step || 1,
      data_steps: data.num_steps >> 0,
      is_time: is_time
    });
  });
}
<|endoftext|>


<commit_before>/* global formatters, validators, forEach */
define('toDateString', ['isString', 'isNumber', 'isDate'], function (isString, isNumber, isDate) {

    var slice = [].slice,
        push = [].push;

    var DATETIME_FORMATS = {
        MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December'.split(','),
        SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
        AMPMS: ['AM', 'PM'],
        medium: 'MMM d, y h:mm:ss a',
        short: 'M/d/yy h:mm a',
        fullDate: 'EEEE, MMMM d, y',
        longDate: 'MMMM d, y',
        mediumDate: 'MMM d, y',
        shortDate: 'M/d/yy',
        mediumTime: 'h:mm:ss a',
        shortTime: 'h:mm a'
    };

    function int(val) {
        return parseInt(val, 10);
    }

    function uppercase(val) {
        return (val + '').toUpperCase();
    }

    function concat(array1, array2, index) {
        return array1.concat(slice.call(array2, index));
    }

    function padNumber(num, digits, trim) {
        var neg = '';
        if (num < 0) {
            neg = '-';
            num = -num;
        }
        num = '' + num;
        while (num.length < digits) {
            num = '0' + num;
        }
        if (trim) {
            num = num.substr(num.length - digits);
        }
        return neg + num;
    }

    function dateGetter(name, size, offset, trim) {
        offset = offset || 0;
        return function (date) {
            var value = date['get' + name]();
            if (offset > 0 || value > -offset) {
                value += offset;
            }
            if (value === 0 && offset === -12) {
                value = 12;
            }
            return padNumber(value, size, trim);
        };
    }

    function dateStrGetter(name, shortForm) {
        return function (date, formats) {
            var value = date['get' + name]();
            var get = uppercase(shortForm ? ('SHORT' + name) : name);

            return formats[get][value];
        };
    }

    function timeZoneGetter(date) {
        var zone = -1 * date.getTimezoneOffset();
        var paddedZone = (zone >= 0) ? "+" : "";

        paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
        padNumber(Math.abs(zone % 60), 2);

        return paddedZone;
    }

    function getFirstThursdayOfYear(year) {
        // 0 = index of January
        var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
        // 4 = index of Thursday (+1 to account for 1st = 5)
        // 11 = index of *next* Thursday (+1 account for 1st = 12)
        return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
    }

    function getThursdayThisWeek(datetime) {
        return new Date(datetime.getFullYear(), datetime.getMonth(),
            // 4 = index of Thursday
            datetime.getDate() + (4 - datetime.getDay()));
    }

    function weekGetter(size) {
        return function (date) {
            var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
                thisThurs = getThursdayThisWeek(date);

            var diff = +thisThurs - +firstThurs,
                result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week

            return padNumber(result, size);
        };
    }

    function ampmGetter(date, formats) {
        return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
    }

    var DATE_FORMATS = {
        yyyy: dateGetter('FullYear', 4),
        yy: dateGetter('FullYear', 2, 0, true),
        y: dateGetter('FullYear', 1),
        MMMM: dateStrGetter('Month'),
        MMM: dateStrGetter('Month', true),
        MM: dateGetter('Month', 2, 1),
        M: dateGetter('Month', 1, 1),
        dd: dateGetter('Date', 2),
        d: dateGetter('Date', 1),
        HH: dateGetter('Hours', 2),
        H: dateGetter('Hours', 1),
        hh: dateGetter('Hours', 2, -12),
        h: dateGetter('Hours', 1, -12),
        mm: dateGetter('Minutes', 2),
        m: dateGetter('Minutes', 1),
        ss: dateGetter('Seconds', 2),
        s: dateGetter('Seconds', 1),
        // while ISO 8601 requires fractions to be prefixed with `.` or `,`
        // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
        sss: dateGetter('Milliseconds', 3),
        EEEE: dateStrGetter('Day'),
        EEE: dateStrGetter('Day', true),
        a: ampmGetter,
        Z: timeZoneGetter,
        ww: weekGetter(2),
        w: weekGetter(1)
    };

    var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,
        NUMBER_STRING = /^\-?\d+$/;

    /**
     * @ngdoc filter
     * @name date
     * @kind function
     *
     * @description
     *   Formats `date` to a string based on the requested `format`.
     *
     *   `format` string can be composed of the following elements:
     *
     *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
     *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
     *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
     *   * `'MMMM'`: Month in year (January-December)
     *   * `'MMM'`: Month in year (Jan-Dec)
     *   * `'MM'`: Month in year, padded (01-12)
     *   * `'M'`: Month in year (1-12)
     *   * `'dd'`: Day in month, padded (01-31)
     *   * `'d'`: Day in month (1-31)
     *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
     *   * `'EEE'`: Day in Week, (Sun-Sat)
     *   * `'HH'`: Hour in day, padded (00-23)
     *   * `'H'`: Hour in day (0-23)
     *   * `'hh'`: Hour in am/pm, padded (01-12)
     *   * `'h'`: Hour in am/pm, (1-12)
     *   * `'mm'`: Minute in hour, padded (00-59)
     *   * `'m'`: Minute in hour (0-59)
     *   * `'ss'`: Second in minute, padded (00-59)
     *   * `'s'`: Second in minute (0-59)
     *   * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
     *   * `'a'`: am/pm marker
     *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
     *   * `'ww'`: ISO-8601 week of year (00-53)
     *   * `'w'`: ISO-8601 week of year (0-53)
     *
     *   `format` string can also be one of the following predefined
     *   {@link guide/i18n localizable formats}:
     *
     *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
     *     (e.g. Sep 3, 2010 12:05:08 pm)
     *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 pm)
     *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
     *     (e.g. Friday, September 3, 2010)
     *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
     *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
     *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
     *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
     *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
     *
     *   `format` string can contain literal values. These need to be quoted with single quotes (e.g.
     *   `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
     *   (e.g. `"h 'o''clock'"`).
     *
     * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
     *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
     *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
     *    specified in the string input, the time is considered to be in the local timezone.
     * @param {string=} format Formatting rules (see Description). If not specified,
     *    `mediumDate` is used.
     * @returns {string} Formatted string or the input if input is not recognized as date/millis.
     *
     * @example
     <example>
     <file name="index.html">
     {{1288323623006 | date:'medium'}}:
     {{1288323623006 | date:'medium'}}

     {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}:
     {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}

     {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}:
     {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}

     </file>
     <file name="protractor.js" type="protractor">
     it('should format date', function() {
         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
     });
     </file>
     </example>
     */
    var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
// 1        2       3         4          5          6          7          8  9     10      11
    function jsonStringToDate(string) {
        var match = string.match(R_ISO8601_STR);
        if (match) {
            var date = new Date(0),
                tzHour = 0,
                tzMin = 0,
                dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
                timeSetter = match[8] ? date.setUTCHours : date.setHours;

            if (match[9]) {
                tzHour = int(match[9] + match[10]);
                tzMin = int(match[9] + match[11]);
            }
            dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
            var h = int(match[4] || 0) - tzHour;
            var m = int(match[5] || 0) - tzMin;
            var s = int(match[6] || 0);
            var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
            timeSetter.call(date, h, m, s, ms);
            return date;
        }
        return string;
    }

    var toDateString = function (date, format) {
        var text = '',
            parts = [],
            fn, match;

        format = format || 'mediumDate';
        format = DATETIME_FORMATS[format] || format;
        if (isString(date)) {
            date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
        }

        if (isNumber(date)) {
            date = new Date(date);
        }

        if (!isDate(date)) {
            return date;
        }

        while (format) {
            match = DATE_FORMATS_SPLIT.exec(format);
            if (match) {
                parts = concat(parts, match, 1);
                format = parts.pop();
            } else {
                parts.push(format);
                format = null;
            }
        }

        forEach(parts, function (value) {
            fn = DATE_FORMATS[value];
            text += fn ? fn(date, DATETIME_FORMATS) : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
        });

        return text;
    };

    return toDateString;

});
<commit_msg>fixed bad reference to forEach in toDateString<commit_after>/* global formatters, validators, forEach */
define('toDateString', ['isString', 'isNumber', 'isDate', 'each'], function (isString, isNumber, isDate, each) {

    var slice = [].slice,
        push = [].push;

    var DATETIME_FORMATS = {
        MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December'.split(','),
        SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
        AMPMS: ['AM', 'PM'],
        medium: 'MMM d, y h:mm:ss a',
        short: 'M/d/yy h:mm a',
        fullDate: 'EEEE, MMMM d, y',
        longDate: 'MMMM d, y',
        mediumDate: 'MMM d, y',
        shortDate: 'M/d/yy',
        mediumTime: 'h:mm:ss a',
        shortTime: 'h:mm a'
    };

    function int(val) {
        return parseInt(val, 10);
    }

    function uppercase(val) {
        return (val + '').toUpperCase();
    }

    function concat(array1, array2, index) {
        return array1.concat(slice.call(array2, index));
    }

    function padNumber(num, digits, trim) {
        var neg = '';
        if (num < 0) {
            neg = '-';
            num = -num;
        }
        num = '' + num;
        while (num.length < digits) {
            num = '0' + num;
        }
        if (trim) {
            num = num.substr(num.length - digits);
        }
        return neg + num;
    }

    function dateGetter(name, size, offset, trim) {
        offset = offset || 0;
        return function (date) {
            var value = date['get' + name]();
            if (offset > 0 || value > -offset) {
                value += offset;
            }
            if (value === 0 && offset === -12) {
                value = 12;
            }
            return padNumber(value, size, trim);
        };
    }

    function dateStrGetter(name, shortForm) {
        return function (date, formats) {
            var value = date['get' + name]();
            var get = uppercase(shortForm ? ('SHORT' + name) : name);

            return formats[get][value];
        };
    }

    function timeZoneGetter(date) {
        var zone = -1 * date.getTimezoneOffset();
        var paddedZone = (zone >= 0) ? "+" : "";

        paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
        padNumber(Math.abs(zone % 60), 2);

        return paddedZone;
    }

    function getFirstThursdayOfYear(year) {
        // 0 = index of January
        var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
        // 4 = index of Thursday (+1 to account for 1st = 5)
        // 11 = index of *next* Thursday (+1 account for 1st = 12)
        return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
    }

    function getThursdayThisWeek(datetime) {
        return new Date(datetime.getFullYear(), datetime.getMonth(),
            // 4 = index of Thursday
            datetime.getDate() + (4 - datetime.getDay()));
    }

    function weekGetter(size) {
        return function (date) {
            var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
                thisThurs = getThursdayThisWeek(date);

            var diff = +thisThurs - +firstThurs,
                result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week

            return padNumber(result, size);
        };
    }

    function ampmGetter(date, formats) {
        return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
    }

    var DATE_FORMATS = {
        yyyy: dateGetter('FullYear', 4),
        yy: dateGetter('FullYear', 2, 0, true),
        y: dateGetter('FullYear', 1),
        MMMM: dateStrGetter('Month'),
        MMM: dateStrGetter('Month', true),
        MM: dateGetter('Month', 2, 1),
        M: dateGetter('Month', 1, 1),
        dd: dateGetter('Date', 2),
        d: dateGetter('Date', 1),
        HH: dateGetter('Hours', 2),
        H: dateGetter('Hours', 1),
        hh: dateGetter('Hours', 2, -12),
        h: dateGetter('Hours', 1, -12),
        mm: dateGetter('Minutes', 2),
        m: dateGetter('Minutes', 1),
        ss: dateGetter('Seconds', 2),
        s: dateGetter('Seconds', 1),
        // while ISO 8601 requires fractions to be prefixed with `.` or `,`
        // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
        sss: dateGetter('Milliseconds', 3),
        EEEE: dateStrGetter('Day'),
        EEE: dateStrGetter('Day', true),
        a: ampmGetter,
        Z: timeZoneGetter,
        ww: weekGetter(2),
        w: weekGetter(1)
    };

    var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,
        NUMBER_STRING = /^\-?\d+$/;

    /**
     * @ngdoc filter
     * @name date
     * @kind function
     *
     * @description
     *   Formats `date` to a string based on the requested `format`.
     *
     *   `format` string can be composed of the following elements:
     *
     *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
     *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
     *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
     *   * `'MMMM'`: Month in year (January-December)
     *   * `'MMM'`: Month in year (Jan-Dec)
     *   * `'MM'`: Month in year, padded (01-12)
     *   * `'M'`: Month in year (1-12)
     *   * `'dd'`: Day in month, padded (01-31)
     *   * `'d'`: Day in month (1-31)
     *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
     *   * `'EEE'`: Day in Week, (Sun-Sat)
     *   * `'HH'`: Hour in day, padded (00-23)
     *   * `'H'`: Hour in day (0-23)
     *   * `'hh'`: Hour in am/pm, padded (01-12)
     *   * `'h'`: Hour in am/pm, (1-12)
     *   * `'mm'`: Minute in hour, padded (00-59)
     *   * `'m'`: Minute in hour (0-59)
     *   * `'ss'`: Second in minute, padded (00-59)
     *   * `'s'`: Second in minute (0-59)
     *   * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
     *   * `'a'`: am/pm marker
     *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
     *   * `'ww'`: ISO-8601 week of year (00-53)
     *   * `'w'`: ISO-8601 week of year (0-53)
     *
     *   `format` string can also be one of the following predefined
     *   {@link guide/i18n localizable formats}:
     *
     *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
     *     (e.g. Sep 3, 2010 12:05:08 pm)
     *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 pm)
     *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
     *     (e.g. Friday, September 3, 2010)
     *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
     *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
     *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
     *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
     *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
     *
     *   `format` string can contain literal values. These need to be quoted with single quotes (e.g.
     *   `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
     *   (e.g. `"h 'o''clock'"`).
     *
     * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
     *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
     *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
     *    specified in the string input, the time is considered to be in the local timezone.
     * @param {string=} format Formatting rules (see Description). If not specified,
     *    `mediumDate` is used.
     * @returns {string} Formatted string or the input if input is not recognized as date/millis.
     *
     * @example
     <example>
     <file name="index.html">
     {{1288323623006 | date:'medium'}}:
     {{1288323623006 | date:'medium'}}

     {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}:
     {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}

     {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}:
     {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}

     </file>
     <file name="protractor.js" type="protractor">
     it('should format date', function() {
         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
     });
     </file>
     </example>
     */
    var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
// 1        2       3         4          5          6          7          8  9     10      11
    function jsonStringToDate(string) {
        var match = string.match(R_ISO8601_STR);
        if (match) {
            var date = new Date(0),
                tzHour = 0,
                tzMin = 0,
                dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
                timeSetter = match[8] ? date.setUTCHours : date.setHours;

            if (match[9]) {
                tzHour = int(match[9] + match[10]);
                tzMin = int(match[9] + match[11]);
            }
            dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
            var h = int(match[4] || 0) - tzHour;
            var m = int(match[5] || 0) - tzMin;
            var s = int(match[6] || 0);
            var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
            timeSetter.call(date, h, m, s, ms);
            return date;
        }
        return string;
    }

    var toDateString = function (date, format) {
        var text = '',
            parts = [],
            fn, match;

        format = format || 'mediumDate';
        format = DATETIME_FORMATS[format] || format;
        if (isString(date)) {
            date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
        }

        if (isNumber(date)) {
            date = new Date(date);
        }

        if (!isDate(date)) {
            return date;
        }

        while (format) {
            match = DATE_FORMATS_SPLIT.exec(format);
            if (match) {
                parts = concat(parts, match, 1);
                format = parts.pop();
            } else {
                parts.push(format);
                format = null;
            }
        }

        each(parts, function (value) {
            fn = DATE_FORMATS[value];
            text += fn ? fn(date, DATETIME_FORMATS) : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
        });

        return text;
    };

    return toDateString;

});
<|endoftext|>


<commit_before>function TagForm( name, parent ) {
	var self = this;
	self.name = ko.observable( name );
	self.parent = ko.observable( parent );
}

function TagViewModel() {
	var self = this;

	self.tagForm = new TagForm( "", null );
	self.tags = ko.observableArray( [] );
	self.successfulTagAlerts = ko.observableArray( [] );
	self.articles = ko.observableArray( [] );

	self.chosenTag = ko.observable();

	self.goToTag = function( tag ) {
		$.getJSON( "/liber-services/tags/" + tag.id,
					function( tag ) {
						self.tags( tag.childTags );
						$.getJSON( "/liber-services/tags/" + tag.id + "/articles", self.articles );
					} );
	};

	self.createTag = function() {
		var tag = { name: self.tagForm.name(), parent: self.tagForm.parent() };
		$.ajax(
			{
				url: "/liber-services/tags",
				type: "POST",
				data: tag,
				success: function( tag ) {
					self.tags.push( tag );
					self.successfulTagAlerts.push( tag );
				}
			}
		);
	};

	$.getJSON( "/liber-services/tags?parent=", self.tags );
	$.getJSON( "/liber-services/tags/articles", self.articles );
}

$(document).ready(
	function() {
		ko.applyBindings( new TagViewModel() );
	}
);

function getLink( object, relationship ) {
	for( var i = 0; i < object.links.length; i++ ) {
		if( object.links[i].relationship == relationship ) {
			return object.links[i].url;
		}
	}
}<commit_msg>navigating tags and then creating tags now creates tags with the proper parent<commit_after>function TagForm( name, parent ) {
	var self = this;
	self.name = ko.observable( name );
	self.parent = ko.observable( parent );
}

function TagViewModel() {
	var self = this;

	self.tagForm = new TagForm( "", null );
	self.tags = ko.observableArray( [] );
	self.successfulTagAlerts = ko.observableArray( [] );
	self.articles = ko.observableArray( [] );

	self.chosenTag = ko.observable();

	self.goToTag = function( tag ) {
		$.getJSON( "/liber-services/tags/" + tag.id,
					function( tag ) {
						self.tags( tag.childTags );
						self.tagForm.parent( tag.id );
						$.getJSON( "/liber-services/tags/" + tag.id + "/articles", self.articles );
					} );
	};

	self.createTag = function() {
		var tag = { name: self.tagForm.name(), parent: self.tagForm.parent() };
		$.ajax(
			{
				url: "/liber-services/tags",
				type: "POST",
				data: tag,
				success: function( tag ) {
					self.tags.push( tag );
					self.successfulTagAlerts.push( tag );
				}
			}
		);
	};

	$.getJSON( "/liber-services/tags?parent=", self.tags );
	$.getJSON( "/liber-services/tags/articles", self.articles );
}

$(document).ready(
	function() {
		ko.applyBindings( new TagViewModel() );
	}
);

function getLink( object, relationship ) {
	for( var i = 0; i < object.links.length; i++ ) {
		if( object.links[i].relationship == relationship ) {
			return object.links[i].url;
		}
	}
}<|endoftext|>


<commit_before>/**
 * Copyright Victor Berchet
 *
 * This file is part of VisuGps3
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

/**
 * @fileoverview Load event.
 * @author Victor Berchet <victor@suumit.com>
 */

goog.provide('vgps3.track.LoadEvent');

goog.require('goog.events.Event');

/**
 * @param {number} trackIndex
 * @param {vgps3.track.GpsFixes} fixes
 * @param {string} TrackColor
 * @param {String} url
 *
 * @constructor
 * @extends {goog.events.Event}
 */
vgps3.track.LoadEvent = function(trackIndex, fixes, TrackColor, url) {
  goog.base(this, vgps3.track.EventType.LOAD);
  /**
   * @type {String} track url
   */
  this.url = url;

  /**
  * @type {Object} The fixes
  */
  this.fixes = fixes;

  /**
   * @type {number} The track index
  */
  this.trackIndex = trackIndex;

  /**
   * @type {string} The track color
   */
  this.trackColor = TrackColor;
};
goog.inherits(vgps3.track.LoadEvent, goog.events.Event);

<commit_msg>fix a comppiler warning (String->string)<commit_after>/**
 * Copyright Victor Berchet
 *
 * This file is part of VisuGps3
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

/**
 * @fileoverview Load event.
 * @author Victor Berchet <victor@suumit.com>
 */

goog.provide('vgps3.track.LoadEvent');

goog.require('goog.events.Event');

/**
 * @param {number} trackIndex
 * @param {vgps3.track.GpsFixes} fixes
 * @param {string} TrackColor
 * @param {string} url
 *
 * @constructor
 * @extends {goog.events.Event}
 */
vgps3.track.LoadEvent = function(trackIndex, fixes, TrackColor, url) {
  goog.base(this, vgps3.track.EventType.LOAD);
  /**
   * @type {string} track url
   */
  this.url = url;

  /**
  * @type {Object} The fixes
  */
  this.fixes = fixes;

  /**
   * @type {number} The track index
  */
  this.trackIndex = trackIndex;

  /**
   * @type {string} The track color
   */
  this.trackColor = TrackColor;
};
goog.inherits(vgps3.track.LoadEvent, goog.events.Event);

<|endoftext|>