<commit_before>define(function(require) {

    'use strict';

    var $    = require('jquery');
    var PIXI = require('pixi');

    var PixiView         = require('common/v3/pixi/view');
                           require('common/v3/pixi/extensions');
    var WavelengthColors = require('common/colors/wavelength');
    var Colors           = require('common/colors/colors');

    var PEffectSimulation = require('models/simulation');

    var Assets    = require('assets');
    var Constants = require('constants');

    var BeamView = PixiView.extend({

        initialize: function(options) {
            this.mvt = options.mvt;
            this.simulation = options.simulation;

            this.initGraphics();

            this.listenTo(this.model, 'change:wavelength',       this.drawLight);
            this.listenTo(this.model, 'change:photonsPerSecond', this.drawLight);

            this.listenTo(this.simulation, 'change:viewMode', this.drawLight);
        },

        initGraphics: function() {
            this.beamLightGraphics = new PIXI.Graphics();
            this.lampLightGraphics = new PIXI.Graphics();

            this.flashlight = Assets.createSprite(Assets.Images.FLASHLIGHT);
            this.flashlight.anchor.x = 1;
            this.flashlight.anchor.y = 0.5;

            this.flashlightLayer = new PIXI.Container();
            this.flashlightLayer.addChild(this.lampLightGraphics);
            this.flashlightLayer.addChild(this.flashlight);

            this.displayObject.addChild(this.beamLightGraphics);
            this.displayObject.addChild(this.flashlightLayer);

            this.updateMVT(this.mvt);
        },

        /**
         * Updates the model-view-transform and anything that relies on it.
         */
        updateMVT: function(mvt) {
            this.mvt = mvt;

            // Position and rotation of the flashlight layer
            this.updatePosition(this.model, this.model.get('position'));
            this.updateRotation();

            // Update the flashlight position and scale relative to the flashlight layer
            var targetWidth = this.mvt.modelToViewDeltaX(161);
            var scale = targetWidth / this.flashlight.width;
            this.flashlight.scale.x = scale;
            this.flashlight.scale.y = scale;
            this.flashlight.x = this.getLampRadiusA();

            // Transform the light-beam piecewise curve into view space and save it
            this.lightCurve = mvt.modelToView(this.model.getBounds());

            this.drawLight();
        },

        updatePosition: function(model, position) {
            var viewPosition = this.mvt.modelToView(position);
            this.flashlightLayer.x = viewPosition.x;
            this.flashlightLayer.y = viewPosition.y;
        },

        updateRotation: function() {
            this.flashlightLayer.rotation = this.model.getDirection();
        },

        drawLight: function() {
            var graphics = this.lightGraphics;
            var beam = this.model;
            var color = Colors.parseHex(WavelengthColors.nmToHex(beam.get('wavelength')));
            var minLevel = 200;
            var colorMax = 255;
            // The power function here controls the ramp-up of actualColor intensity
            var level = Math.max(minLevel, colorMax - Math.floor((colorMax - minLevel) * Math.pow((beam.get('photonsPerSecond') / beam.get('maxPhotonsPerSecond')), 0.3)));
            var alpha = (colorMax - level) / colorMax;

            this.beamLightGraphics.clear();
            this.lampLightGraphics.clear();

            if (this.simulation.get('viewMode') === PEffectSimulation.BEAM_VIEW) {
                // Draw light beam
                this.beamLightGraphics.beginFill(color, alpha);
                this.beamLightGraphics.drawPiecewiseCurve(this.lightCurve);
                this.beamLightGraphics.endFill();
            }

            // Draw the ellipse filling the flashlight with full saturation.
            var radiusA = this.getLampRadiusA();
            this.lampLightGraphics.beginFill(color, 1);
            this.lampLightGraphics.drawEllipse(0, 0, radiusA, this.flashlight.height / 2);
            this.lampLightGraphics.endFill();
        },

        getLampRadiusA: function() {
            return this.flashlight.width * (6 / 161);
        }

    }, Constants.BeamView);

    return BeamView;
});
<commit_msg>Discovered where in the original a mask was defined for the beam that cut it off after reaching the electrode, so I implemented that, but I still don't know if the photons that make it past the electrode are supposed to make it through or not.  I don't see any masking in the view for that, but perhaps I just haven't found it yet.<commit_after>define(function(require) {

    'use strict';

    var $    = require('jquery');
    var PIXI = require('pixi');

    var PixiView         = require('common/v3/pixi/view');
                           require('common/v3/pixi/extensions');
    var WavelengthColors = require('common/colors/wavelength');
    var Colors           = require('common/colors/colors');

    var PEffectSimulation = require('models/simulation');

    var Assets    = require('assets');
    var Constants = require('constants');

    var BeamView = PixiView.extend({

        initialize: function(options) {
            this.mvt = options.mvt;
            this.simulation = options.simulation;

            this.initGraphics();

            this.listenTo(this.model, 'change:wavelength',       this.drawLight);
            this.listenTo(this.model, 'change:photonsPerSecond', this.drawLight);

            this.listenTo(this.simulation, 'change:viewMode', this.drawLight);
        },

        initGraphics: function() {
            this.beamLightGraphics = new PIXI.Graphics();
            this.lampLightGraphics = new PIXI.Graphics();
            this.mask = new PIXI.Graphics();

            this.flashlight = Assets.createSprite(Assets.Images.FLASHLIGHT);
            this.flashlight.anchor.x = 1;
            this.flashlight.anchor.y = 0.5;

            this.flashlightLayer = new PIXI.Container();
            this.flashlightLayer.addChild(this.lampLightGraphics);
            this.flashlightLayer.addChild(this.flashlight);

            this.displayObject.addChild(this.beamLightGraphics);
            this.displayObject.addChild(this.flashlightLayer);
            this.displayObject.addChild(this.mask);

            this.displayObject.mask = this.mask;

            this.updateMVT(this.mvt);
        },

        /**
         * Updates the model-view-transform and anything that relies on it.
         */
        updateMVT: function(mvt) {
            this.mvt = mvt;

            // Update mask
            this.mask.clear();
            this.mask.beginFill();
            this.mask.drawRect(this.mvt.modelToViewX(this.simulation.leftHandPlate.getX()), 0, 2000, 2000);
            this.mask.endFill();

            // Position and rotation of the flashlight layer
            this.updatePosition(this.model, this.model.get('position'));
            this.updateRotation();

            // Update the flashlight position and scale relative to the flashlight layer
            var targetWidth = this.mvt.modelToViewDeltaX(161);
            var scale = targetWidth / this.flashlight.width;
            this.flashlight.scale.x = scale;
            this.flashlight.scale.y = scale;
            this.flashlight.x = this.getLampRadiusA();

            // Transform the light-beam piecewise curve into view space and save it
            this.lightCurve = mvt.modelToView(this.model.getBounds());

            this.drawLight();
        },

        updatePosition: function(model, position) {
            var viewPosition = this.mvt.modelToView(position);
            this.flashlightLayer.x = viewPosition.x;
            this.flashlightLayer.y = viewPosition.y;
        },

        updateRotation: function() {
            this.flashlightLayer.rotation = this.model.getDirection();
        },

        drawLight: function() {
            var graphics = this.lightGraphics;
            var beam = this.model;
            var color = Colors.parseHex(WavelengthColors.nmToHex(beam.get('wavelength')));
            var minLevel = 200;
            var colorMax = 255;
            // The power function here controls the ramp-up of actualColor intensity
            var level = Math.max(minLevel, colorMax - Math.floor((colorMax - minLevel) * Math.pow((beam.get('photonsPerSecond') / beam.get('maxPhotonsPerSecond')), 0.3)));
            var alpha = (colorMax - level) / colorMax;

            this.beamLightGraphics.clear();
            this.lampLightGraphics.clear();

            if (this.simulation.get('viewMode') === PEffectSimulation.BEAM_VIEW) {
                // Draw light beam
                this.beamLightGraphics.beginFill(color, alpha);
                this.beamLightGraphics.drawPiecewiseCurve(this.lightCurve);
                this.beamLightGraphics.endFill();
            }

            // Draw the ellipse filling the flashlight with full saturation.
            var radiusA = this.getLampRadiusA();
            this.lampLightGraphics.beginFill(color, 1);
            this.lampLightGraphics.drawEllipse(0, 0, radiusA, this.flashlight.height / 2);
            this.lampLightGraphics.endFill();
        },

        getLampRadiusA: function() {
            return this.flashlight.width * (6 / 161);
        }

    }, Constants.BeamView);

    return BeamView;
});
<|endoftext|>


<commit_before>'use babel'

import {CompositeDisposable} from 'sb-event-kit'
import Communication from 'sb-communication'

export default class Exchange {
  constructor(worker) {
    this.worker = worker
    this.port = worker.port || worker

    this.communication = new Communication()
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(this.communication)

    const callback = message => {
      this.communication.parseMessage(message)
    }
    this.worker.addEventListener('message', callback)
    this.subscriptions.add({
      dispose: () => {
        this.worker.removeEventListener('message', callback)
      }
    })
    this.communication.onShouldSend(message => {
      this.port.postMessage(message)
    })

    this.onRequest('ping', function(_, message) {
      message.response = 'pong'
    })
  }

  request(name, data = {}) {
    return this.communication.request(name, data)
  }

  onRequest(name, callback) {
    return this.communication.onRequest(name, callback)
  }

  terminate() {
    this.worker.terminate()
    this.dispose()
  }
  dispose() {
    this.subscriptions.dispose()
  }

  static create(filePath) {
    return new Exchange(new Worker(filePath))
  }
  static createShared(filePath) {
    const worker = new SharedWorker(filePath)
    worker.port.start()
    return new Exchange(worker)
  }
}
<commit_msg>:new: Use disposable instead of object in exchange-main<commit_after>'use babel'

import {Disposable, CompositeDisposable} from 'sb-event-kit'
import Communication from 'sb-communication'

export default class Exchange {
  constructor(worker) {
    this.worker = worker
    this.port = worker.port || worker

    this.communication = new Communication()
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(this.communication)

    const callback = message => {
      this.communication.parseMessage(message)
    }
    this.worker.addEventListener('message', callback)
    this.subscriptions.add(new Disposable(() => {
      this.worker.removeEventListener('message', callback)
    }))
    this.communication.onShouldSend(message => {
      this.port.postMessage(message)
    })

    this.onRequest('ping', function(_, message) {
      message.response = 'pong'
    })
  }

  request(name, data = {}) {
    return this.communication.request(name, data)
  }

  onRequest(name, callback) {
    return this.communication.onRequest(name, callback)
  }

  terminate() {
    this.worker.terminate()
    this.dispose()
  }
  dispose() {
    this.subscriptions.dispose()
  }

  static create(filePath) {
    return new Exchange(new Worker(filePath))
  }
  static createShared(filePath) {
    const worker = new SharedWorker(filePath)
    worker.port.start()
    return new Exchange(worker)
  }
}
<|endoftext|>


<commit_before>
var jqCore = function (root, selector) {
    var _this = this;
    this.items = [];
    var tps = typeof(root) + "_" + typeof(selector);
    switch (tps) {
        case "string_undefined":
            this.selector = root;
            this.root = cc.director.getRunningScene();
            this.items = CocosSelector(this.root, this.selector);
            ; break;

        case "object_undefined":
            var objInfo=Object.prototype.toString.call(root)
            if(objInfo=="[object Array]"){
                this.items = root;
            }else{
                this.items = [root];
            }
            ; break;

        case "object_string":
            this.selector = selector;
            this.root = root;
            this.items = CocosSelector(this.root, this.selector);
            ; break;

        case "undefined_undefined":;break;
        default: return; break;

    }

    this.length = this.items.length;
    for (var i = 0; i < this.items.length; i++) {
        this[i] = this.items[i];
    }

};

 <commit_msg>增加 jsb支持<commit_after> var jqCore = function(root, selector) {
     var _this = this;
     this.items = [];

     var tps = Object.prototype.toString.call(root) + "_" + Object.prototype.toString.call(selector);

     if (cc.sys.isNative) {
         switch (tps) {
             case "[object String]_[object Undefined]":
                 this.selector = root;
                 this.root = cc.director.getRunningScene();
                 this.items = CocosSelector(this.root, this.selector);;
                 break;
             case "[object Node]_[object Undefined]":
                 this.items = [root];;
                 break;
             case "[object Array]_[object Undefined]":
                 this.items = root;;
                 break;
             case "[object Node]_[object String]":
                 this.selector = selector;
                 this.root = root;
                 this.items = CocosSelector(this.root, this.selector);;
                 break;
             case "[object Undefined]_[object Undefined]":
                 ;
                 break;
             default:
                 return;
                 break;

         }

     } else {
         switch (tps) {
             case "[object String]_[object Undefined]":
                 this.selector = root;
                 this.root = cc.director.getRunningScene();
                 this.items = CocosSelector(this.root, this.selector);;
                 break;
             case "[object Object]_[object Undefined]":
                 this.items = [root];;
                 break;
             case "[object Array]_[object Undefined]":
                 this.items = root;;
                 break;
             case "[object Object]_[object String]":
                 this.selector = selector;
                 this.root = root;
                 this.items = CocosSelector(this.root, this.selector);;
                 break;
             case "[object Undefined]_[object Undefined]":
                 ;
                 break;
             default:
                 return;
                 break;

         }

     }

     this.length = this.items.length;
     for (var i = 0; i < this.items.length; i++) {
         this[i] = this.items[i];
     }

 };
<|endoftext|>


<commit_before>
var messages = {
    en: {
        // dummy/h2-foo
        "dummy.h2-foo.not-found":   "Selector h2.foo not found."
        // dummy/h1
    ,   "dummy.h1.not-found":       "Selector h1 not found."
        // dummy/dahut
    ,   "dummy.dahut.not-found":    "No element containing DAHUT."
        // headers/title
    ,   "headers.title.not-found":  "Document does not have a title."
        // headers/logo
    ,   "headers.logo.not-found":   "Failed to include logo in link with absolute URLs."
        // headers/subm-logo
    ,   "headers.subm-logo.not-found":  "Failed to include logo in link with absolute URLs for ${type} Submission."
        // headers/hr
    ,   "headers.hr.not-found":     "No <hr> at the end of, or right after, div.head."
    ,   "headers.hr.duplicate":     "Duplicate <hr> at the end of, and right after, div.head."
        // headers/h2-status
    ,   "headers.h2-status.no-h2":  "No status+date h2 element."
    ,   "headers.h2-status.bad-h2": "Incorrect status h2 header."
        // headers/h1-title
    ,   "headers.h1-title.title":   "Content of title and h1 do not match."
        // headers/dl
    ,   "headers.dl.no-dd":                     "No <dd> element found for '${dt}'."
    ,   "headers.dl.this-version":              "This Version is missing."
    ,   "headers.dl.latest-version":            "Latest Version is missing."
    ,   "headers.dl.previous-version":          "Previous Version is missing."
    ,   "headers.dl.previous-not-needed":       "Previous Version is included but does not seem necessary."
    ,   "headers.dl.this-latest-order":         "This Version must be before Latest Version."
    ,   "headers.dl.latest-previous-order":     "Latest Version must be before Previous Version."
    ,   "headers.dl.this-link":                 "Link href and text differ for This Version."
    ,   "headers.dl.this-date":                 "Mismatch between document date and This Version link."
    ,   "headers.dl.no-date":                   "Cannot find document date."
    ,   "headers.dl.this-syntax":               "Wrong syntax for This Version link."
    ,   "headers.dl.latest-link":               "Link href and text differ for Latest Version."
    ,   "headers.dl.this-latest-shortname":     "Short names differ between This and Latest Versions."
    ,   "headers.dl.latest-syntax":             "Wrong syntax for Latest Version link."
    ,   "headers.dl.previous-link":             "Link href and text differ for Previous Version."
    ,   "headers.dl.this-previous-shortname":   "Short names differ between This and Previous Versions."
    ,   "headers.dl.previous-syntax":           "Wrong syntax for Previous Version link."
    ,   "headers.dl.editor":                    "There must be at least an editor or an author."
    ,   "headers.dl.rescinds":                  "Rescinds this Recommendation is missing."
    ,   "headers.dl.rescinds-not-needed":       "Rescinds this Recommendation is included but does not seem necessary."
    ,   "headers.dl.latest-rescinds-order":     "Latest Version must be before Rescinds this Recommendation."
    ,   "headers.dl.rescinds-link":             "Link href and text differ for Rescinds this Recommendation."
    ,   "headers.dl.this-rescinds-shortname":   "Short names differ between This Version and Rescinds this Recommendation."
    ,   "headers.dl.rescinds-syntax":           "Wrong syntax for Rescinds this Recommendation link."
        // headers/errata
    ,   "headers.errata.not-found":             "Errata paragraph not found."
        // headers/translations
    ,   "headers.translations.not-found":       "Translations paragraph not found."
        // headers/div-head
    ,   "headers.div-head.not-found":           "No div.head found."
        // headers/copyright
    ,   "headers.copyright.no-match":           "Copyright string does not match requirements."
    ,   "headers.copyright.kitten-friendly":    "Using CC-BY kitten-friendly license."
    ,   "headers.copyright.link-text":          "Missing link '${href}' with text '${text}'."
    ,   "headers.copyright.not-found":          "Missing copyright paragraph."
        // links/internal
    ,   "links.internal.anchor":    "Link to missing anchor: '${id}'."
        // links/linkchecker
    ,   "links.linkchecker.display":   "Please verify using Link Checker."
        // links/compound
    ,   "links.compound.skipped":           "HTML and CSS validations were skipped"
    ,   "links.compound.no-validation":     "${file} HTML CSS"
    ,   "links.compound.link":              "${file} HTML ${markup} CSS ${css}"
    ,   "links.compound.error":             "${file} HTML CSS: ${errMsg}"
        // sotd/supersedable
    ,   "sotd.supersedable.no-sotd":        "No SotD section."
    ,   "sotd.supersedable.no-sotd-intro":  "No SotD introduction."
    ,   "sotd.supersedable.no-sotd-tr":     "No SotD introduction link to TR."
        // sotd/submission
    ,   "sotd.submission.no-sotd":              "No SotD section."
    ,   "sotd.submission.no-submission-text":   "No member submission paragraph."
    ,   "sotd.submission.link-text":            "Missing link '${href}' with text '${text}'."
    ,   "sotd.submission.no-sm-link":           "Missing link 'http://www.w3.org/Submission/...' with text 'Submitting Members'."
    ,   "sotd.submission.no-tc-link":           "Missing link 'http://www.w3.org/Submission/...' with text 'W3C Team Comment'."
        // sotd/team-subm-link
    ,   "sotd.team-subm-link.no-sotd":  "No SotD section."
    ,   "sotd.team-subm-link.no-link":  "No link to all Team Submissions."
        // sotd/stability
    ,   "sotd.stability.no-sotd":       "No SotD section."
    ,   "sotd.stability.no-stability":  "No stability warning paragraph."
    ,   "sotd.stability.no-rec-review": "No indication that the Director, developers, and interested parties have reviewed this Recommendation."
        // sotd/status
    ,   "sotd.status.no-sotd":      "No SotD section."
    ,   "sotd.status.no-mention":   "SotD does not mention that this is a ${status}."
        // sotd/review-end
    ,   "sotd.review-end.no-sotd":      "No SotD section."
    ,   "sotd.review-end.not-found":    "No review end date found."
    ,   "sotd.review-end.found":        "Review end date found: ${date}"
        // sotd/cr-end
    ,   "sotd.cr-end.no-sotd":      "No SotD section."
    ,   "sotd.cr-end.not-found":    "No minimal CR end date found."
        // sotd/pp
    ,   "sotd.pp.no-sotd":          "No SotD section."
    ,   "sotd.pp.no-pp":            "No patent policy paragraph."
    ,   "sotd.pp.no-feb5":          "No link to Feb 5 PP."
    ,   "sotd.pp.no-disclosures":   "No link to public list of disclosures."
    ,   "sotd.pp.no-claims":        "No link to definition of essential claims."
    ,   "sotd.pp.no-section6":      "No link to section 6."
        // sotd/charter-disclosure
    ,   "sotd.charter-disclosure.no-sotd":      "No SotD section."
    ,   "sotd.charter-disclosure.not-found":    "No link to charter for disclosure obligations found."
        // sotd/mailing-list
    ,   "sotd.mailing-list.no-sotd":    "No SotD section."
    ,   "sotd.mailing-list.no-list":    "No mailing list link."
    ,   "sotd.mailing-list.no-sub":     "No mailing list subscription link."
    ,   "sotd.mailing-list.no-arch":    "No mailing list archives link."
        // sotd/group-name
    ,   "sotd.group-name.no-sotd":      "No SotD section."
    ,   "sotd.group-name.no-ml":        "No email address found with which to check group name."
    ,   "sotd.group-name.no-group":     "No group matching email address found in groups database."
    ,   "sotd.group-name.no-link":      "No link found to group (${name}, ${url})."
    ,   "sotd.group-name.no-name":      "Link to group found with wrong group name (${name}, ${url})."
        // sotd/processDocument
    ,   "sotd.processDocument.not-specified":     "No process document specified."
    ,   "sotd.processDocument.not-found":         "According to your selection, the document must mention the governing process: ${process}"
        // sotd/implementation
    ,   "sotd.implementation.candidate":    "Found link to a preliminary interoperability/implementation report: ${text}."
    ,   "sotd.implementation.no-report":    "Found a statement about a (missing) preliminary interoperability/implementation report: “…${text}…”."
    ,   "sotd.implementation.unknown":      "Could not find statements nor links about preliminary implementation/interoperability reports."
        // sotd/ac-review
    ,   "sotd.ac-review.found":       "Found a link to an online questionnaire: ${link}. Does this link provide access to the review form?"
    ,   "sotd.ac-review.not-found":   "Did not find information about a forum for AC Representative feedback."
        // structure/section-ids
    ,   "structure.section-ids.no-id":  "No ID for section: '${text}'."
        // structure/h2
    ,   "structure.h2.abstract":    "First <h2> after div.head must be 'Abstract', was '${was}'."
    ,   "structure.h2.sotd":        "Second <h2> after div.head must be 'Status of This Document', was '${was}'."
    ,   "structure.h2.toc":         "Third <h2> after div.head must be 'Table of Contents', was '${was}'."
        // style/sheet
    ,   "style.sheet.last":         "W3C TR style sheet must be last."
    ,   "style.sheet.not-found":    "Missing W3C TR style sheet."
        // validation/css
    ,   "validation.css.skipped":       "CSS validation was skipped."
    ,   "validation.css.no-source":     "No CSS source to validate."
    ,   "validation.css.failure":       "Failure code from CSS validator: ${status}."
    ,   "validation.css.no-response":   "No response from CSS validator."
    ,   "validation.css.warning":       "[${level}:${type}] ${message} (${source}, line ${line})"
    ,   "validation.css.error":         "[${type}] ${message} (${source}, line ${line} in '${context}')"
        // validation/html
    ,   "validation.html.skipped":              "HTML validation was skipped."
    ,   "validation.html.no-source":            "No HTML source to validate."
    ,   "validation.html.failure":              "Failure code from HTML validator: ${status}."
    ,   "validation.html.no-response":          "No response from HTML validator."
    ,   "validation.html.error":                "[${line}@${column}] ${message}"
    ,   "validation.html.warning":              "${message}"
    ,   "validation.html.non-document-error":   "[${subType}] ${message}"
	// heuristic/group
    ,   "heuristic.group.candidate":          "The group might be the ${name}."
    ,   "heuristic.group.not-found":          "Could not identify a group behind this spec."
    }
};

exports.message = function (lang, rule, key, extra) {
    if (!messages[lang]) return "@@@No such language: " + lang + "@@@";
    var l10n = messages[lang][rule + "." + key];
    if (!l10n) return "@@@No such entry: " + rule + "." + key + "@@@";
    if (extra) {
        return l10n.replace(/\$\{([^\}]+)\}/g, function (m, p1) {
            if (extra.hasOwnProperty(p1)) return extra[p1];
            return "@@@No such data: ${" + p1 + "}@@@";
        });
    }
    return l10n;
};
<commit_msg>missing message for processDocument rule<commit_after>
var messages = {
    en: {
        // dummy/h2-foo
        "dummy.h2-foo.not-found":   "Selector h2.foo not found."
        // dummy/h1
    ,   "dummy.h1.not-found":       "Selector h1 not found."
        // dummy/dahut
    ,   "dummy.dahut.not-found":    "No element containing DAHUT."
        // headers/title
    ,   "headers.title.not-found":  "Document does not have a title."
        // headers/logo
    ,   "headers.logo.not-found":   "Failed to include logo in link with absolute URLs."
        // headers/subm-logo
    ,   "headers.subm-logo.not-found":  "Failed to include logo in link with absolute URLs for ${type} Submission."
        // headers/hr
    ,   "headers.hr.not-found":     "No <hr> at the end of, or right after, div.head."
    ,   "headers.hr.duplicate":     "Duplicate <hr> at the end of, and right after, div.head."
        // headers/h2-status
    ,   "headers.h2-status.no-h2":  "No status+date h2 element."
    ,   "headers.h2-status.bad-h2": "Incorrect status h2 header."
        // headers/h1-title
    ,   "headers.h1-title.title":   "Content of title and h1 do not match."
        // headers/dl
    ,   "headers.dl.no-dd":                     "No <dd> element found for '${dt}'."
    ,   "headers.dl.this-version":              "This Version is missing."
    ,   "headers.dl.latest-version":            "Latest Version is missing."
    ,   "headers.dl.previous-version":          "Previous Version is missing."
    ,   "headers.dl.previous-not-needed":       "Previous Version is included but does not seem necessary."
    ,   "headers.dl.this-latest-order":         "This Version must be before Latest Version."
    ,   "headers.dl.latest-previous-order":     "Latest Version must be before Previous Version."
    ,   "headers.dl.this-link":                 "Link href and text differ for This Version."
    ,   "headers.dl.this-date":                 "Mismatch between document date and This Version link."
    ,   "headers.dl.no-date":                   "Cannot find document date."
    ,   "headers.dl.this-syntax":               "Wrong syntax for This Version link."
    ,   "headers.dl.latest-link":               "Link href and text differ for Latest Version."
    ,   "headers.dl.this-latest-shortname":     "Short names differ between This and Latest Versions."
    ,   "headers.dl.latest-syntax":             "Wrong syntax for Latest Version link."
    ,   "headers.dl.previous-link":             "Link href and text differ for Previous Version."
    ,   "headers.dl.this-previous-shortname":   "Short names differ between This and Previous Versions."
    ,   "headers.dl.previous-syntax":           "Wrong syntax for Previous Version link."
    ,   "headers.dl.editor":                    "There must be at least an editor or an author."
    ,   "headers.dl.rescinds":                  "Rescinds this Recommendation is missing."
    ,   "headers.dl.rescinds-not-needed":       "Rescinds this Recommendation is included but does not seem necessary."
    ,   "headers.dl.latest-rescinds-order":     "Latest Version must be before Rescinds this Recommendation."
    ,   "headers.dl.rescinds-link":             "Link href and text differ for Rescinds this Recommendation."
    ,   "headers.dl.this-rescinds-shortname":   "Short names differ between This Version and Rescinds this Recommendation."
    ,   "headers.dl.rescinds-syntax":           "Wrong syntax for Rescinds this Recommendation link."
        // headers/errata
    ,   "headers.errata.not-found":             "Errata paragraph not found."
        // headers/translations
    ,   "headers.translations.not-found":       "Translations paragraph not found."
        // headers/div-head
    ,   "headers.div-head.not-found":           "No div.head found."
        // headers/copyright
    ,   "headers.copyright.no-match":           "Copyright string does not match requirements."
    ,   "headers.copyright.kitten-friendly":    "Using CC-BY kitten-friendly license."
    ,   "headers.copyright.link-text":          "Missing link '${href}' with text '${text}'."
    ,   "headers.copyright.not-found":          "Missing copyright paragraph."
        // links/internal
    ,   "links.internal.anchor":    "Link to missing anchor: '${id}'."
        // links/linkchecker
    ,   "links.linkchecker.display":   "Please verify using Link Checker."
        // links/compound
    ,   "links.compound.skipped":           "HTML and CSS validations were skipped"
    ,   "links.compound.no-validation":     "${file} HTML CSS"
    ,   "links.compound.link":              "${file} HTML ${markup} CSS ${css}"
    ,   "links.compound.error":             "${file} HTML CSS: ${errMsg}"
        // sotd/supersedable
    ,   "sotd.supersedable.no-sotd":        "No SotD section."
    ,   "sotd.supersedable.no-sotd-intro":  "No SotD introduction."
    ,   "sotd.supersedable.no-sotd-tr":     "No SotD introduction link to TR."
        // sotd/submission
    ,   "sotd.submission.no-sotd":              "No SotD section."
    ,   "sotd.submission.no-submission-text":   "No member submission paragraph."
    ,   "sotd.submission.link-text":            "Missing link '${href}' with text '${text}'."
    ,   "sotd.submission.no-sm-link":           "Missing link 'http://www.w3.org/Submission/...' with text 'Submitting Members'."
    ,   "sotd.submission.no-tc-link":           "Missing link 'http://www.w3.org/Submission/...' with text 'W3C Team Comment'."
        // sotd/team-subm-link
    ,   "sotd.team-subm-link.no-sotd":  "No SotD section."
    ,   "sotd.team-subm-link.no-link":  "No link to all Team Submissions."
        // sotd/stability
    ,   "sotd.stability.no-sotd":       "No SotD section."
    ,   "sotd.stability.no-stability":  "No stability warning paragraph."
    ,   "sotd.stability.no-rec-review": "No indication that the Director, developers, and interested parties have reviewed this Recommendation."
        // sotd/status
    ,   "sotd.status.no-sotd":      "No SotD section."
    ,   "sotd.status.no-mention":   "SotD does not mention that this is a ${status}."
        // sotd/review-end
    ,   "sotd.review-end.no-sotd":      "No SotD section."
    ,   "sotd.review-end.not-found":    "No review end date found."
    ,   "sotd.review-end.found":        "Review end date found: ${date}"
        // sotd/cr-end
    ,   "sotd.cr-end.no-sotd":      "No SotD section."
    ,   "sotd.cr-end.not-found":    "No minimal CR end date found."
        // sotd/pp
    ,   "sotd.pp.no-sotd":          "No SotD section."
    ,   "sotd.pp.no-pp":            "No patent policy paragraph."
    ,   "sotd.pp.no-feb5":          "No link to Feb 5 PP."
    ,   "sotd.pp.no-disclosures":   "No link to public list of disclosures."
    ,   "sotd.pp.no-claims":        "No link to definition of essential claims."
    ,   "sotd.pp.no-section6":      "No link to section 6."
        // sotd/charter-disclosure
    ,   "sotd.charter-disclosure.no-sotd":      "No SotD section."
    ,   "sotd.charter-disclosure.not-found":    "No link to charter for disclosure obligations found."
        // sotd/mailing-list
    ,   "sotd.mailing-list.no-sotd":    "No SotD section."
    ,   "sotd.mailing-list.no-list":    "No mailing list link."
    ,   "sotd.mailing-list.no-sub":     "No mailing list subscription link."
    ,   "sotd.mailing-list.no-arch":    "No mailing list archives link."
        // sotd/group-name
    ,   "sotd.group-name.no-sotd":      "No SotD section."
    ,   "sotd.group-name.no-ml":        "No email address found with which to check group name."
    ,   "sotd.group-name.no-group":     "No group matching email address found in groups database."
    ,   "sotd.group-name.no-link":      "No link found to group (${name}, ${url})."
    ,   "sotd.group-name.no-name":      "Link to group found with wrong group name (${name}, ${url})."
        // sotd/processDocument
    ,   "sotd.processDocument.no-sotd":           "No SotD section."
    ,   "sotd.processDocument.not-specified":     "No process document specified."
    ,   "sotd.processDocument.not-found":         "According to your selection, the document must mention the governing process: ${process}"
        // sotd/implementation
    ,   "sotd.implementation.candidate":    "Found link to a preliminary interoperability/implementation report: ${text}."
    ,   "sotd.implementation.no-report":    "Found a statement about a (missing) preliminary interoperability/implementation report: “…${text}…”."
    ,   "sotd.implementation.unknown":      "Could not find statements nor links about preliminary implementation/interoperability reports."
        // sotd/ac-review
    ,   "sotd.ac-review.found":       "Found a link to an online questionnaire: ${link}. Does this link provide access to the review form?"
    ,   "sotd.ac-review.not-found":   "Did not find information about a forum for AC Representative feedback."
        // structure/section-ids
    ,   "structure.section-ids.no-id":  "No ID for section: '${text}'."
        // structure/h2
    ,   "structure.h2.abstract":    "First <h2> after div.head must be 'Abstract', was '${was}'."
    ,   "structure.h2.sotd":        "Second <h2> after div.head must be 'Status of This Document', was '${was}'."
    ,   "structure.h2.toc":         "Third <h2> after div.head must be 'Table of Contents', was '${was}'."
        // style/sheet
    ,   "style.sheet.last":         "W3C TR style sheet must be last."
    ,   "style.sheet.not-found":    "Missing W3C TR style sheet."
        // validation/css
    ,   "validation.css.skipped":       "CSS validation was skipped."
    ,   "validation.css.no-source":     "No CSS source to validate."
    ,   "validation.css.failure":       "Failure code from CSS validator: ${status}."
    ,   "validation.css.no-response":   "No response from CSS validator."
    ,   "validation.css.warning":       "[${level}:${type}] ${message} (${source}, line ${line})"
    ,   "validation.css.error":         "[${type}] ${message} (${source}, line ${line} in '${context}')"
        // validation/html
    ,   "validation.html.skipped":              "HTML validation was skipped."
    ,   "validation.html.no-source":            "No HTML source to validate."
    ,   "validation.html.failure":              "Failure code from HTML validator: ${status}."
    ,   "validation.html.no-response":          "No response from HTML validator."
    ,   "validation.html.error":                "[${line}@${column}] ${message}"
    ,   "validation.html.warning":              "${message}"
    ,   "validation.html.non-document-error":   "[${subType}] ${message}"
	// heuristic/group
    ,   "heuristic.group.candidate":          "The group might be the ${name}."
    ,   "heuristic.group.not-found":          "Could not identify a group behind this spec."
    }
};

exports.message = function (lang, rule, key, extra) {
    if (!messages[lang]) return "@@@No such language: " + lang + "@@@";
    var l10n = messages[lang][rule + "." + key];
    if (!l10n) return "@@@No such entry: " + rule + "." + key + "@@@";
    if (extra) {
        return l10n.replace(/\$\{([^\}]+)\}/g, function (m, p1) {
            if (extra.hasOwnProperty(p1)) return extra[p1];
            return "@@@No such data: ${" + p1 + "}@@@";
        });
    }
    return l10n;
};
<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */

var config    = require('./config.js');
var ldapjs    = require('ldapjs');
var log       = require('logule').init(module, "ldap");
var util      = require('util');

/**
 * Initialize ldap
 *
 * @constructor
 */

function ldap() {

  this.connected = false;

  /**
   * ldap client
   */

  this.client = ldapjs.createClient({
    url: util.format('ldap://%s/', config.ldap.host),
    connectTimeout: 5
  });

  this.client.on('connect', function(socket) {
   this.connected = true;
  });

  this.client.on('connectTimeout', function() {
    console.log("connect timeout");
  });

  this.client.on('connectError', function(err) {
    console.log(err);
  });

  this.client.on('setupError', function(err) {
    console.log(err);
  });

  // ..

  log.info('init');
}

/**
 * Find user for usbSerial `id` or nfcID `id`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForDevice = function(id, fn) {
  this._query(util.format('(|(usbSerial=%s)(nfcID=%s))', id, id), fn);
};

/**
 * Find user for usbSerial `device`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForUSBSerial = function(serial, fn) {
  this._query(util.format('usbSerial=%s', serial), fn);
};

/**
 * Find user for nfcID `device`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForNFCID = function(id, fn) {
  this._query(util.format('nfcID=%s', id), fn);
};

/**
 * Generic LDAP query
 *
 * @param device
 * @param fn
 */

ldap.prototype._query = function(filter, fn) {
  var opts = {
    filter: filter,
    scope: 'one',
    timeLimit: 2
  };
  var entries = [];

  this.client.search("ou=people," + config.ldap.basedn, opts, function (err, res) {
    if (err) fn(err);
    res.on('searchEntry', function (entry) {
      entries.push(entry.object);
    });
    res.on('error', function(err) {
      console.error('error: ' + err.message);
    });
    res.on('end', function(result) {
      console.log('status=%d, num_entries=%d', result.status, entries.length);
      console.log('entries => %j', entries);
      fn(false, entries[0]);
    });
  });
};

/**
 * Expose `ldap`
 * @type {ldap}
 */

module.exports = new ldap();
<commit_msg>adding handlers for ldap connection events<commit_after>
/**
 * Module dependencies
 */

var config    = require('./config.js');
var ldapjs    = require('ldapjs');
var log       = require('logule').init(module, "ldap");
var util      = require('util');

/**
 * Initialize ldap
 *
 * @constructor
 */

function ldap() {

  this.connected = false;

  /**
   * ldap client
   */

  this.client = ldapjs.createClient({
    url: util.format('ldap://%s/', config.ldap.host),
    connectTimeout: 5
  });

  /**
   * ldap event handlers
   */

  this.client.on('connect', self._handleConnect.bind(self));
  this.client.on('connectTimeout', self._handleConnectTimeout.bind(self));
  this.client.on('connectError', self._handleConnectError.bind(self));

  // ..

  log.info('init');
}

/**
 *
 * @param socket
 * @private
 */

ldap.prototype._handleConnect = function(socket) {
  this.connected = true;
};

/**
 *
 * @private
 */

ldap.prototype._handleConnectTimeout = function() {
  log.error('connection timed out');
};

/**
 *
 * @private
 */

ldap.prototype._handleConnectError = function(error) {
  log.error('error connecting');
};

/**
 * Find user for usbSerial `id` or nfcID `id`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForDevice = function(id, fn) {
  this._query(util.format('(|(usbSerial=%s)(nfcID=%s))', id, id), fn);
};

/**
 * Find user for usbSerial `device`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForUSBSerial = function(serial, fn) {
  this._query(util.format('usbSerial=%s', serial), fn);
};

/**
 * Find user for nfcID `device`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForNFCID = function(id, fn) {
  this._query(util.format('nfcID=%s', id), fn);
};

/**
 * Generic LDAP query
 *
 * @param device
 * @param fn
 */

ldap.prototype._query = function(filter, fn) {
  var opts = {
    filter: filter,
    scope: 'one',
    timeLimit: 2
  };
  var entries = [];

  this.client.search("ou=people," + config.ldap.basedn, opts, function (err, res) {
    if (err) fn(err);
    res.on('searchEntry', function (entry) {
      entries.push(entry.object);
    });
    res.on('error', function(err) {
      console.error('error: ' + err.message);
    });
    res.on('end', function(result) {
      console.log('status=%d, num_entries=%d', result.status, entries.length);
      console.log('entries => %j', entries);
      fn(false, entries[0]);
    });
  });
};

/**
 * Expose `ldap`
 * @type {ldap}
 */

module.exports = new ldap();
<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';
import Modal from 'react-native-modalbox';
import Icon from 'react-native-vector-icons/Ionicons';
import { StyleSheet, TouchableOpacity, View, Text } from 'react-native';
import globalStyles, { DARK_GREY, WARM_GREY, SUSSOL_ORANGE } from '../../globalStyles';
import { AutocompleteSelector, PageInfo, Button, ToggleBar } from '..';
import { PageContentModal } from './PageContentModal';
import { SETTINGS_KEYS } from '../../settings';

export class ByProgramModal extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      searchIsOpen: false,
      searchModalKey: 'supplier',
      isProgramOrder: true,
    };
  }

  onOrderToggle = () => {
    const { isProgramOrder } = this.state;
    this.setState({ isProgramOrder: !isProgramOrder });
  };

  getToggleBarProps = () => {
    const { type, isProgramOrder } = this.state;
    const buttonText = type === 'requisition' ? 'order' : 'stocktake';
    return [
      {
        text: `Program ${buttonText}`,
        onPress: this.onOrderToggle,
        isOn: isProgramOrder,
      },
      {
        text: `General ${buttonText}`,
        onPress: this.onOrderToggle,
        isOn: !isProgramOrder,
      },
    ];
  };

  // Should capitalize, can be multiple words. Split on
  // white space, map returning word with capitalized first
  // letter, return joined. Same with main modal title
  getSearchModalTitle = () => {
    const { searchModalKey } = this.state;
    return `Select a ${searchModalKey} to use`;
  };

  getPageInfoProps = () => {
    const { values } = this.props;
    const { program, supplier, orderType, period } = values;
    return {
      program: {
        info: `${program.name || 'Select the program to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'program' });
        },
        editableType: 'selectable',
      },
      supplier: {
        title: '',
        info: `${supplier.name || 'Select the supplier to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'supplier' });
        },
        editableType: 'selectable',
      },
      orderTypes: {
        title: '',
        info: `${orderType.name || 'Select the order type to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'orderType' });
        },
        editableType: 'selectable',
      },
      period: {
        title: '',
        info: `${period.name || 'Select the Period to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'period' });
        },
        editableType: 'selectable',
      },
    };
  };

  selectSearchValue = valueSetterParams => {
    const { valueSetter } = this.props;
    this.setState({ searchIsOpen: false });
    valueSetter(valueSetterParams);
  };

  getSearchModalOptions = () => {
    const { program, orderType, database, settings } = this.props;
    const tags = settings.get(SETTINGS_KEYS.THIS_STORE_TAGS);
    const { periodScheduleName, orderTypes } = program.getStoreTagObject(tags);

    return {
      program: database.objects('MasterList').filter(masterList => {
        if (!masterList.canUseProgram()) return null;
        return masterList;
      }),
      supplier: database.objects('Name').filtered('isSupplier = $0', true),
      orderType: orderTypes,
      period: database
        .objects('PeriodSchedule')
        .filtered('name = $0', periodScheduleName)[0]
        .getUsablePeriodsForProgram(orderType.maxOrdersPerPeriod),
    };
  };

  getSearchModalProps = () => {
    const { program, supplier, orderType } = this.props;
    return {
      program: {
        options: this.getSearchModalOptions().program,
        queryString: 'name BEGINSWITH[c] $0 ',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'program', item }),
        renderLeftText: item => `${item.name}`,
      },
      supplier: {
        options: program.name ? this.getSearchModalOptions().supplier : [],
        queryString: 'name BEGINSWITH[c] $0 OR code BEGINSWITH[c] $0',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'supplier', item }),
        renderLeftText: item => `${item.name}`,
        renderRightText: item => `(${item.code})`,
      },
      orderType: {
        options: supplier.name ? this.getSearchModalOptions().orderType : [],
        queryString: 'name BEGINSWITH[c] $0',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'orderType', item }),
        renderLeftText: item => `${item.name}`,
        rightRightText: item => `${item.maxMOS} ${item.thresholdMOS}`,
      },
      period: {
        options: orderType.name ? this.getSearchModalOptions().period : [],
        queryString: 'name BEGINSWITH[c] $0',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'period', item }),
        renderLeftText: item => `${item.name}`,
        renderRightText: item =>
          `${item.startDate} - ${item.endDate} -- (${item.numberOfRequisitionsForProgram(
            program
          )}) requisitions`,
      },
    };
  };

  onSearchModalConfirm = ({ key, item }) => {
    this.setState({ searchIsOpen: false, [key]: item });
  };

  onSearchModalCancel = () => {
    this.setState({ searchIsOpen: false });
  };

  renderSearchModal = () => {
    const { searchModalKey } = this.state;
    const { database } = this.props;
    const { [searchModalKey]: modalProps } = this.getSearchModalProps(database);
    return (
      <AutocompleteSelector
        options={modalProps.options}
        queryString={modalProps.queryString}
        sortByString={modalProps.sortByString}
        onSelect={modalProps.onSelect}
        renderLeftText={modalProps.renderLeftText}
        renderRightText={modalProps.renderRightText}
      />
    );
  };

  renderPageInfo = () => {
    const { isProgramOrder } = this.state;
    const { type, program, supplier } = this.props;
    return (
      <>
        {isProgramOrder && (
          <PageInfo columns={[[this.getPageInfoProps().program]]} isEditingDisabled={false} />
        )}
        <PageInfo
          columns={[[this.getPageInfoProps().supplier]]}
          isEditingDisabled={program || !isProgramOrder}
        />
        {type === 'requisition' && isProgramOrder && (
          <>
            <PageInfo
              columns={[[this.getPageInfoProps().orderTypes]]}
              isEditingDisabled={program && supplier}
            />
            <PageInfo columns={[[this.getPageInfoProps().period]]} isEditingDisabled={false} />
          </>
        )}
      </>
    );
  };

  render() {
    const { onConfirm, onCancel, isOpen, type, program, supplier, period, orderType } = this.props;
    const { searchIsOpen } = this.state;

    return (
      <Modal
        isOpen={isOpen}
        style={[globalStyles.modal, localStyles.modal]}
        backdropPressToClose={false}
        backdropOpacity={0.88}
        swipeToClose={false}
        position="top"
        coverScreen
      >
        <TouchableOpacity onPress={onCancel} style={localStyles.closeButton}>
          <Icon name="md-close" style={localStyles.closeIcon} />
        </TouchableOpacity>

        <Text style={localStyles.title}>{`${type} details`}</Text>

        <View style={localStyles.toggleContainer}>
          <ToggleBar
            style={globalStyles.toggleBar}
            textOffStyle={globalStyles.toggleText}
            textOnStyle={globalStyles.toggleTextSelected}
            toggleOffStyle={globalStyles.toggleOption}
            toggleOnStyle={globalStyles.toggleOptionSelected}
            toggles={this.getToggleBarProps()}
          />
        </View>

        <View style={localStyles.contentContainer}>{this.renderPageInfo()}</View>
        <View style={localStyles.grow} />
        <View style={localStyles.bottomContainer}>
          <Button
            text="CANCEL"
            onPress={onCancel}
            disabledColor={WARM_GREY}
            isDisabled={false}
            style={globalStyles.button}
            textStyle={globalStyles.buttonText}
          />
          <Button
            text="OK"
            onPress={onConfirm}
            disabledColor={WARM_GREY}
            isDisabled={!(program.name && supplier.name && period.name && orderType.name)}
            style={[globalStyles.button, localStyles.OKButton]}
            textStyle={[globalStyles.buttonText, localStyles.OKButtonText]}
          />
        </View>

        <PageContentModal
          isOpen={searchIsOpen}
          onClose={this.onSearchModalCancel}
          title={this.getSearchModalTitle()}
          coverScreen
        >
          {this.renderSearchModal()}
        </PageContentModal>
      </Modal>
    );
  }
}

export default ByProgramModal;

const localStyles = StyleSheet.create({
  modal: {
    flex: 1,
    flexDirection: 'column',
    backgroundColor: DARK_GREY,
    opacity: 1,
    zIndex: 1,
    position: 'absolute',
    overflow: 'hidden',
    flexGrow: 1,
  },
  toggleContainer: {
    width: 292,
    alignSelf: 'center',
    marginTop: 20,
  },
  title: {
    fontSize: 18,
    color: 'white',
    textAlign: 'center',
    marginBottom: 10,
    marginTop: 10,
  },
  contentContainer: {
    width: '50%',
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'flex-start',
    maxHeight: '30%',
    marginTop: '5%',
  },
  closeIcon: {
    fontSize: 36,
    color: 'white',
  },
  closeButton: {
    position: 'absolute',
    top: 10,
    right: 20,
  },
  OKButton: {
    backgroundColor: SUSSOL_ORANGE,
  },
  OKButtonText: {
    color: 'white',
    fontSize: 16,
  },
  bottomContainer: {
    bottom: 0,
    right: 0,
    position: 'absolute',
    flex: 1,
    flexDirection: 'row',
  },
  grow: {
    flexGrow: 2,
  },
});

ByProgramModal.propTypes = {
  database: PropTypes.object.isRequired,
  onConfirm: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
  isOpen: PropTypes.bool.isRequired,
  type: PropTypes.string.isRequired,
  valueSetter: PropTypes.func.isRequired,
  values: PropTypes.object.isRequired,
  settings: PropTypes.object.isRequired,
  program: PropTypes.object.isRequired,
  orderType: PropTypes.object.isRequired,
  supplier: PropTypes.object.isRequired,
  period: PropTypes.object.isRequired,
};
<commit_msg>Add update to byProgramModal to be more controlled component<commit_after>/* eslint-disable indent */
/* eslint-disable prefer-destructuring */
/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';
import Modal from 'react-native-modalbox';
import Icon from 'react-native-vector-icons/Ionicons';
import { StyleSheet, TouchableOpacity, View, Text } from 'react-native';
import globalStyles, { DARK_GREY, WARM_GREY, SUSSOL_ORANGE } from '../../globalStyles';
import { AutocompleteSelector, PageInfo, Button, ToggleBar } from '..';
import { PageContentModal } from './PageContentModal';
import { SETTINGS_KEYS } from '../../settings';

export class ByProgramModal extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      searchIsOpen: false,
      searchModalKey: 'supplier',
    };
  }

  getToggleBarProps = () => {
    const { onToggleChange, isProgramOrder, type } = this.props;
    const buttonText = type === 'requisition' ? 'order' : 'stocktake';
    return [
      {
        text: `Program ${buttonText}`,
        onPress: onToggleChange,
        isOn: isProgramOrder,
      },
      {
        text: `General ${buttonText}`,
        onPress: onToggleChange,
        isOn: !isProgramOrder,
      },
    ];
  };

  // Should capitalize, can be multiple words. Split on
  // white space, map returning word with capitalized first
  // letter, return joined. Same with main modal title
  getSearchModalTitle = () => {
    const { searchModalKey } = this.state;
    return `Select a ${searchModalKey} to use`;
  };

  selectSearchValue = ({ key, item }) => {
    const { valueSetter } = this.props;
    this.setState({ searchIsOpen: false });
    valueSetter({ key, item });
  };

  getSearchModalOptions = () => {
    const { programValues, settings, database } = this.props;
    const { program, orderType } = programValues;
    const tags = settings.get(SETTINGS_KEYS.THIS_STORE_TAGS);
    let orderTypes;
    let periodScheduleName;
    if (program.name) {
      const storeTagObject = program.getStoreTagObject(tags);
      orderTypes = storeTagObject.orderTypes;
      periodScheduleName = storeTagObject.periodScheduleName;
    }

    return {
      programOptions: database
        .objects('MasterList')
        .filtered('isProgram = $0', true)
        .filter(masterList => masterList.canUseProgram(tags)),
      supplierOptions: database.objects('Name').filtered('isSupplier = $0', true),
      orderTypeOptions: orderTypes,
      periodOptions: periodScheduleName
        ? database
            .objects('PeriodSchedule')
            .filtered('name = $0', periodScheduleName)[0]
            .getUseablePeriodsForProgram(program, orderType.maxOrdersPerPeriod)
        : database.objects('Period'),
    };
  };

  getSearchModalProps = () => {
    const { programValues } = this.props;
    const { program } = programValues;
    const {
      programOptions,
      supplierOptions,
      orderTypeOptions,
      periodOptions,
    } = this.getSearchModalOptions();

    return {
      program: {
        options: programOptions,
        queryString: 'name BEGINSWITH[c] $0 ',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'program', item }),
        renderLeftText: item => `${item.name}`,
      },
      supplier: {
        options: supplierOptions,
        queryString: 'name BEGINSWITH[c] $0 OR code BEGINSWITH[c] $0',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'supplier', item }),
        renderLeftText: item => `${item.name}`,
        renderRightText: item => `(${item.code})`,
      },
      orderType: {
        options: orderTypeOptions,
        queryString: 'name BEGINSWITH[c] $0',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'orderType', item }),
        renderLeftText: item => `${item.name}`,
        renderRightText: item =>
          `Max Months of stock: ${item.maxMOS} - Threshold Months of stock: ${item.thresholdMOS}`,
      },
      period: {
        options: periodOptions,
        queryString: 'name BEGINSWITH[c] $0',
        sortByString: 'name',
        onSelect: item => this.selectSearchValue({ key: 'period', item }),
        renderLeftText: item => `${item.name}`,
        renderRightText: item =>
          `${item.getFormattedPeriod()} -- [${item.numberOfRequisitionsForProgram(
            program
          )}] Requisition(s)`,
      },
    };
  };

  onSearchModalConfirm = ({ key, item }) => {
    this.setState({ searchIsOpen: false, [key]: item });
  };

  onSearchModalCancel = () => {
    this.setState({ searchIsOpen: false });
  };

  renderSearchModal = () => {
    const { searchModalKey } = this.state;
    const { database } = this.props;
    const { [searchModalKey]: modalProps } = this.getSearchModalProps(database);
    return (
      <AutocompleteSelector
        options={modalProps.options}
        queryString={modalProps.queryString}
        sortByString={modalProps.sortByString}
        onSelect={modalProps.onSelect}
        renderLeftText={modalProps.renderLeftText}
        renderRightText={modalProps.renderRightText}
      />
    );
  };

  getPageInfoProps = () => {
    const { programValues } = this.props;
    const { program, supplier, orderType, period } = programValues;
    return {
      program: {
        info: `${program.name || 'Select the program to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'program' });
        },
        editableType: 'selectable',
      },
      supplier: {
        title: '',
        info: `${supplier.name || 'Select the supplier to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'supplier' });
        },
        editableType: 'selectable',
      },
      orderTypes: {
        title: '',
        info: `${orderType.name || 'Select the order type to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'orderType' });
        },
        editableType: 'selectable',
      },
      period: {
        title: '',
        info: `${period.name || 'Select the Period to use'}`,
        onPress: () => {
          this.setState({ searchIsOpen: true, searchModalKey: 'period' });
        },
        editableType: 'selectable',
      },
    };
  };

  getProgress = () => {
    const { programValues, isProgramOrder, type } = this.props;
    const { program, supplier, period, orderType } = programValues;
    const complete =
      isProgramOrder && !!program.name && !!supplier.name && !!period.name && !!orderType.name;
    return {
      canConfirm: complete || (!isProgramOrder && supplier.name),
      canSelectSupplier: !(isProgramOrder && !program.name),
      canSelectOrderType: !!supplier.name,
      canSelectPeriod: !!orderType.name,
      isRequisitionProgramOrder: type === 'requisition' && isProgramOrder,
    };
  };

  renderPageInfo = () => {
    const { isProgramOrder } = this.props;
    const {
      canSelectSupplier,
      canSelectOrderType,
      canSelectPeriod,
      isRequisitionProgramOrder,
    } = this.getProgress();
    return (
      <>
        {isProgramOrder && (
          <PageInfo columns={[[this.getPageInfoProps().program]]} isEditingDisabled={false} />
        )}
        <PageInfo
          columns={[[this.getPageInfoProps().supplier]]}
          isEditingDisabled={!canSelectSupplier}
        />
        {isRequisitionProgramOrder && (
          <>
            <PageInfo
              columns={[[this.getPageInfoProps().orderTypes]]}
              isEditingDisabled={!canSelectOrderType}
            />
            <PageInfo
              columns={[[this.getPageInfoProps().period]]}
              isEditingDisabled={!canSelectPeriod}
            />
          </>
        )}
      </>
    );
  };

  render() {
    const { onConfirm, onCancel, isOpen, type } = this.props;
    const { searchIsOpen } = this.state;
    return (
      <Modal
        isOpen={isOpen}
        style={[globalStyles.modal, localStyles.modal]}
        backdropPressToClose={false}
        backdropOpacity={0.88}
        swipeToClose={false}
        position="top"
        coverScreen
      >
        <TouchableOpacity onPress={onCancel} style={localStyles.closeButton}>
          <Icon name="md-close" style={localStyles.closeIcon} />
        </TouchableOpacity>

        <Text style={localStyles.title}>{`${type} details`}</Text>

        <View style={localStyles.toggleContainer}>
          <ToggleBar
            style={globalStyles.toggleBar}
            textOffStyle={globalStyles.toggleText}
            textOnStyle={globalStyles.toggleTextSelected}
            toggleOffStyle={globalStyles.toggleOption}
            toggleOnStyle={globalStyles.toggleOptionSelected}
            toggles={this.getToggleBarProps()}
          />
        </View>

        <View style={localStyles.contentContainer}>{this.renderPageInfo()}</View>
        <View style={localStyles.grow} />
        <View style={localStyles.bottomContainer}>
          <Button
            text="CANCEL"
            onPress={onCancel}
            disabledColor={WARM_GREY}
            isDisabled={false}
            style={globalStyles.button}
            textStyle={globalStyles.buttonText}
          />
          <Button
            text="OK"
            onPress={onConfirm}
            disabledColor={WARM_GREY}
            isDisabled={!this.getProgress().canConfirm}
            style={[globalStyles.button, localStyles.OKButton]}
            textStyle={[globalStyles.buttonText, localStyles.OKButtonText]}
          />
        </View>

        <PageContentModal
          isOpen={searchIsOpen}
          onClose={this.onSearchModalCancel}
          title={this.getSearchModalTitle()}
          coverScreen
        >
          {this.renderSearchModal()}
        </PageContentModal>
      </Modal>
    );
  }
}

export default ByProgramModal;

const localStyles = StyleSheet.create({
  modal: {
    flex: 1,
    flexDirection: 'column',
    backgroundColor: DARK_GREY,
    opacity: 1,
    zIndex: 1,
    position: 'absolute',
    overflow: 'hidden',
    flexGrow: 1,
  },
  toggleContainer: {
    width: 292,
    alignSelf: 'center',
    marginTop: 20,
  },
  title: {
    fontSize: 18,
    color: 'white',
    textAlign: 'center',
    marginBottom: 10,
    marginTop: 10,
  },
  contentContainer: {
    width: '50%',
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'flex-start',
    maxHeight: '30%',
    marginTop: '5%',
  },
  closeIcon: {
    fontSize: 36,
    color: 'white',
  },
  closeButton: {
    position: 'absolute',
    top: 10,
    right: 20,
  },
  OKButton: {
    backgroundColor: SUSSOL_ORANGE,
  },
  OKButtonText: {
    color: 'white',
    fontSize: 16,
  },
  bottomContainer: {
    bottom: 0,
    right: 0,
    position: 'absolute',
    flex: 1,
    flexDirection: 'row',
  },
  grow: {
    flexGrow: 2,
  },
});

ByProgramModal.propTypes = {
  database: PropTypes.object.isRequired,
  onConfirm: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
  isOpen: PropTypes.bool.isRequired,
  type: PropTypes.string.isRequired,
  valueSetter: PropTypes.func.isRequired,
  settings: PropTypes.object.isRequired,
  programValues: PropTypes.object.isRequired,
  isProgramOrder: PropTypes.bool.isRequired,
  onToggleChange: PropTypes.func.isRequired,
};
<|endoftext|>


<commit_before>
// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local

var npm = require("../npm.js")
  , symlink = require("./utils/link.js")
  , fs = require("graceful-fs")
  , log = require("./utils/log.js")
  , asyncMap = require("slide").asyncMap
  , chain = require("slide").chain
  , path = require("path")
  , relativize = require("./utils/relativize.js")
  , rm = require("rimraf")
  , output = require("./utils/output.js")
  , build = require("./build.js")

module.exports = link

link.usage = "npm link (in package dir)"
           + "\nnpm link <pkg> (link global into local)"

link.completion = function (opts, cb) {
  var dir = path.join(npm.config.get("prefix"), "lib", "node_modules")
  fs.readdir(dir, function (er, files) {
    cb(er, files.filter(function (f) {
      return f.charAt(0) !== "."
    }))
  })
}

function link (args, cb) {
  if (npm.config.get("global")) {
    return cb(new Error("link should never be --global.\n"
                       +"Please re-run this command with --local"))
  }
  if (args.length === 1 && args[0] === ".") args = []
  if (args.length) return linkInstall(args, cb)
  linkPkg(npm.prefix, cb)
}

function linkInstall (pkgs, cb) {
  var gp = npm.config.get("prefix")
  asyncMap(pkgs, function (pkg, cb) {
    function n (er, data) {
      if (er) return cb(er, data)
      // install returns [ [folder, pkgId], ... ]
      // but we definitely installed just one thing.
      var d = data.filter(function (d) { return !d[3] })
      pp = d[0][1]
      pkg = path.basename(pp)
      target = path.resolve(npm.dir, pkg)
      next()
    }

    var t = path.resolve(gp, "lib")
      , pp = path.resolve(t, "node_modules", pkg)
      , rp = null
      , target = path.resolve(npm.dir, pkg)

    // if it's a folder or a random not-installed thing, then
    // link or install it first
    if (pkg.indexOf("/") !== -1 || pkg.indexOf("\\") !== -1) {
      return fs.lstat(path.resolve(pkg), function (er, st) {
        if (er || !st.isDirectory()) {
          npm.commands.install(t, pkg, n)
        } else {
          rp = path.resolve(pkg)
          linkPkg(rp, n)
        }
      })
    }

    fs.lstat(pp, function (er, st) {
      if (er) {
        rp = pp
        return npm.commands.install(t, pkg, n)
      } else if (!st.isSymbolicLink()) {
        rp = pp
        next()
      } else {
        return fs.realpath(pp, function (er, real) {
          if (er) log.warn(pkg, "invalid symbolic link")
          else rp = real
          next()
        })
      }
    })

    function next () {
      chain
        ( [ [npm.commands, "unbuild", [target]]
          , [log.verbose, "symlinking " + pp + " to "+target, "link"]
          , [symlink, pp, target]
          // do run lifecycle scripts - full build here.
          , rp && [build, [target]]
          , [ resultPrinter, pkg, pp, target, rp ] ]
        , cb )
    }
  }, cb)
}

function linkPkg (folder, cb_) {
  var gp = npm.config.get("prefix")
    , me = folder || npm.prefix
    , readJson = require("./utils/read-json.js")
  readJson( path.resolve(me, "package.json")
          , { dev: true }
          , function (er, d) {
    function cb (er) {
      return cb_(er, [[d && d._id, target, null, null]])
    }
    if (er) return cb(er)
    var target = path.resolve(gp, "lib", "node_modules", d.name)
    rm(target, function (er) {
      if (er) return cb(er)
      symlink(me, target, function (er) {
        if (er) return cb(er)
        log.verbose(target, "link: build target")
        // also install missing dependencies.
        npm.commands.install(me, [], function (er, installed) {
          if (er) return cb(er)
          // build the global stuff.  Don't run *any* scripts, because
          // install command already will have done that.
          build([target], true, build._noLC, true, function (er) {
            if (er) return cb(er)
            resultPrinter(path.basename(me), me, target, cb)
          })
        })
      })
    })
  })
}

function resultPrinter (pkg, src, dest, rp, cb) {
  if (typeof cb !== "function") cb = rp, rp = null
  var where = relativize(dest, path.resolve(process.cwd(),"x"))
  rp = (rp || "").trim()
  src = (src || "").trim()
  if (npm.config.get("parseable")) {
    return parseableOutput(dest, rp || src, cb)
  }
  if (rp === src) rp = null
  output.write(where+" -> " + src
              +(rp ? " -> " + rp: ""), cb)
}

function parseableOutput (dest, rp, cb) {
  // link is always effectively "long", since it doesn't help much to
  // *just* print the target folder.
  // However, we don't actually ever read the version number, so
  // the second field is always blank.
  output.write(dest + "::" + rp, cb)
}
<commit_msg>npm link not supported on win32<commit_after>
// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local

var npm = require("../npm.js")
  , symlink = require("./utils/link.js")
  , fs = require("graceful-fs")
  , log = require("./utils/log.js")
  , asyncMap = require("slide").asyncMap
  , chain = require("slide").chain
  , path = require("path")
  , relativize = require("./utils/relativize.js")
  , rm = require("rimraf")
  , output = require("./utils/output.js")
  , build = require("./build.js")

module.exports = link

link.usage = "npm link (in package dir)"
           + "\nnpm link <pkg> (link global into local)"

link.completion = function (opts, cb) {
  var dir = path.join(npm.config.get("prefix"), "lib", "node_modules")
  fs.readdir(dir, function (er, files) {
    cb(er, files.filter(function (f) {
      return f.charAt(0) !== "."
    }))
  })
}

function link (args, cb) {
  if (process.platform === "win32") {
    var e = new Error("npm link not supported on windows")
    e.code = "ENOTSUP"
    e.errno = require("constants").ENOTSUP
    return cb(e)
  }

  if (npm.config.get("global")) {
    return cb(new Error("link should never be --global.\n"
                       +"Please re-run this command with --local"))
  }
  if (args.length === 1 && args[0] === ".") args = []
  if (args.length) return linkInstall(args, cb)
  linkPkg(npm.prefix, cb)
}

function linkInstall (pkgs, cb) {
  var gp = npm.config.get("prefix")
  asyncMap(pkgs, function (pkg, cb) {
    function n (er, data) {
      if (er) return cb(er, data)
      // install returns [ [folder, pkgId], ... ]
      // but we definitely installed just one thing.
      var d = data.filter(function (d) { return !d[3] })
      pp = d[0][1]
      pkg = path.basename(pp)
      target = path.resolve(npm.dir, pkg)
      next()
    }

    var t = path.resolve(gp, "lib")
      , pp = path.resolve(t, "node_modules", pkg)
      , rp = null
      , target = path.resolve(npm.dir, pkg)

    // if it's a folder or a random not-installed thing, then
    // link or install it first
    if (pkg.indexOf("/") !== -1 || pkg.indexOf("\\") !== -1) {
      return fs.lstat(path.resolve(pkg), function (er, st) {
        if (er || !st.isDirectory()) {
          npm.commands.install(t, pkg, n)
        } else {
          rp = path.resolve(pkg)
          linkPkg(rp, n)
        }
      })
    }

    fs.lstat(pp, function (er, st) {
      if (er) {
        rp = pp
        return npm.commands.install(t, pkg, n)
      } else if (!st.isSymbolicLink()) {
        rp = pp
        next()
      } else {
        return fs.realpath(pp, function (er, real) {
          if (er) log.warn(pkg, "invalid symbolic link")
          else rp = real
          next()
        })
      }
    })

    function next () {
      chain
        ( [ [npm.commands, "unbuild", [target]]
          , [log.verbose, "symlinking " + pp + " to "+target, "link"]
          , [symlink, pp, target]
          // do run lifecycle scripts - full build here.
          , rp && [build, [target]]
          , [ resultPrinter, pkg, pp, target, rp ] ]
        , cb )
    }
  }, cb)
}

function linkPkg (folder, cb_) {
  var gp = npm.config.get("prefix")
    , me = folder || npm.prefix
    , readJson = require("./utils/read-json.js")
  readJson( path.resolve(me, "package.json")
          , { dev: true }
          , function (er, d) {
    function cb (er) {
      return cb_(er, [[d && d._id, target, null, null]])
    }
    if (er) return cb(er)
    var target = path.resolve(gp, "lib", "node_modules", d.name)
    rm(target, function (er) {
      if (er) return cb(er)
      symlink(me, target, function (er) {
        if (er) return cb(er)
        log.verbose(target, "link: build target")
        // also install missing dependencies.
        npm.commands.install(me, [], function (er, installed) {
          if (er) return cb(er)
          // build the global stuff.  Don't run *any* scripts, because
          // install command already will have done that.
          build([target], true, build._noLC, true, function (er) {
            if (er) return cb(er)
            resultPrinter(path.basename(me), me, target, cb)
          })
        })
      })
    })
  })
}

function resultPrinter (pkg, src, dest, rp, cb) {
  if (typeof cb !== "function") cb = rp, rp = null
  var where = relativize(dest, path.resolve(process.cwd(),"x"))
  rp = (rp || "").trim()
  src = (src || "").trim()
  if (npm.config.get("parseable")) {
    return parseableOutput(dest, rp || src, cb)
  }
  if (rp === src) rp = null
  output.write(where+" -> " + src
              +(rp ? " -> " + rp: ""), cb)
}

function parseableOutput (dest, rp, cb) {
  // link is always effectively "long", since it doesn't help much to
  // *just* print the target folder.
  // However, we don't actually ever read the version number, so
  // the second field is always blank.
  output.write(dest + "::" + rp, cb)
}
<|endoftext|>


<commit_before>/** @module logs */

const path = require('path')
const _ = require('lodash/fp')
const nodeFs = require('fs')
const fs = require('pify')(nodeFs)

const dataPath = require('./data-path')
const logsPath = path.resolve(dataPath, 'log')

const DELIMITER_SIZE = 5
const MAX_SIZE = 100000
const MAX_LINES = 2000

/**
 * Get the newest log files
 *
 * Given a timestamp, get the newest log files
 * that are created after timestamp
 *
 * @name getNewestLogFiles
 * @function
 * @async
 *
 * @param {date} timestamp Requested date as unix timestamp
 *
 * @returns {array} Array of log filenames that exceed the timetamp provided
 */
function getNewestLogFiles (timestamp) {
  return fs.readdir(logsPath).then(files => {
    const time = timestamp.slice(0, 10)
    return _.filter(filename => filename.slice(0, 10) >= time, files)
  })
}

/**
 * Get the older log files
 *
 * Given a timestamp, get the log files
 * that are created on and before the timestamp
 *
 * @name getOlderLogFiles
 * @function
 * @async
 *
 * @param {date} timestamp Requested date as unix timestamp
 *
 * @returns {array} Array of log filenames that are older than the timetamp provided
 */
function getOlderLogFiles (timestamp) {
  return fs.readdir(logsPath).then(files => {
    const time = timestamp.slice(0, 10)
    return _.filter(filename => filename.slice(0, 10) <= time, files)
  })
}

function removeLogFiles (timestamp) {
  const timestampISO = new Date(timestamp).toISOString()
  return getOlderLogFiles(timestampISO)
    .then(files => {
      return Promise.all(_.map(filename => {
        return fs.unlink(path.resolve(logsPath, filename))
      }, files))
    })
}

/**
 * Given a timestamp, get the newest log lines that are created
 * after the requested date
 *
 * @name queryNewestLogs
 * @function
 *
 * @param {date} timestamp Requested date as unix timestamp
 *
 * @returns {array} Array of objects (log lines) in ascending order
 */
function queryNewestLogs (_last) {
  const last = _last || {timestamp: new Date(0).toISOString(), serial: 0}
  const filter = log => [log.timestamp, log.serial] > [last.timestamp, last.serial]

  return getNewestLogFiles(last.timestamp)
  // Read log data from log files
    .then(files => {
      return Promise.all(_.map(filename => {
        return fs.readFile(path.resolve(logsPath, filename))
          .then(fileData => fileData.toString().split('\n'))
      }, files))
    })
    .then(_.flatten)
  // Remove empty lines
    .then(_.filter(line => !_.isEmpty(line)))
  // Parse each line
    .then(_.map(JSON.parse))
  // Filter only logs that are created after the required timestamp
    .then(_.filter(filter))
  // Sort ascending
    .then(_.sortBy(log => ([log.timestamp, log.serial])))
  // Only send last MAX_LINES lines.
  // Ensures syncing is fast at the expensive of possible
  // gaps in server-side log storage.
    .then(_.takeRight(MAX_LINES))
  // Don't send more than MAX_SIZE
  // to avoid any large payload errors
    .then(_.reduce((acc, val) => {
      const size = JSON.stringify(val).length + DELIMITER_SIZE
      return _.concat(acc, {size: _.getOr(0, 'size', _.last(acc)) + size, val})
    }, []))
    .then(_.takeWhile(r => r.size < MAX_SIZE))
    .then(_.map(_.get('val')))
}

module.exports = { removeLogFiles, queryNewestLogs }
<commit_msg>fix: handle invalid chars on log files<commit_after>/** @module logs */

const path = require('path')
const _ = require('lodash/fp')
const nodeFs = require('fs')
const fs = require('pify')(nodeFs)

const dataPath = require('./data-path')
const logsPath = path.resolve(dataPath, 'log')

const DELIMITER_SIZE = 5
const MAX_SIZE = 100000
const MAX_LINES = 2000

/**
 * Get the newest log files
 *
 * Given a timestamp, get the newest log files
 * that are created after timestamp
 *
 * @name getNewestLogFiles
 * @function
 * @async
 *
 * @param {date} timestamp Requested date as unix timestamp
 *
 * @returns {array} Array of log filenames that exceed the timetamp provided
 */
function getNewestLogFiles (timestamp) {
  return fs.readdir(logsPath).then(files => {
    const time = timestamp.slice(0, 10)
    return _.filter(filename => filename.slice(0, 10) >= time, files)
  })
}

/**
 * Get the older log files
 *
 * Given a timestamp, get the log files
 * that are created on and before the timestamp
 *
 * @name getOlderLogFiles
 * @function
 * @async
 *
 * @param {date} timestamp Requested date as unix timestamp
 *
 * @returns {array} Array of log filenames that are older than the timetamp provided
 */
function getOlderLogFiles (timestamp) {
  return fs.readdir(logsPath).then(files => {
    const time = timestamp.slice(0, 10)
    return _.filter(filename => filename.slice(0, 10) <= time, files)
  })
}

function removeLogFiles (timestamp) {
  const timestampISO = new Date(timestamp).toISOString()
  return getOlderLogFiles(timestampISO)
    .then(files => {
      return Promise.all(_.map(filename => {
        return fs.unlink(path.resolve(logsPath, filename))
      }, files))
    })
}

const parseAndHandleInvalid = it => {
  const jsonLike = /\{.*\}/g
  const match = jsonLike.exec(it)
  try {
    return JSON.parse(match)
  } catch (e) {
    return null
  }
}

/**
 * Given a timestamp, get the newest log lines that are created
 * after the requested date
 *
 * @name queryNewestLogs
 * @function
 *
 * @param {date} timestamp Requested date as unix timestamp
 *
 * @returns {array} Array of objects (log lines) in ascending order
 */
function queryNewestLogs (_last) {
  const last = _last || {timestamp: new Date(0).toISOString(), serial: 0}
  const filter = log => [log.timestamp, log.serial] > [last.timestamp, last.serial]

  return getNewestLogFiles(last.timestamp)
  // Read log data from log files
    .then(files => {
      return Promise.all(_.map(filename => {
        return fs.readFile(path.resolve(logsPath, filename))
          .then(fileData => fileData.toString().split('\n'))
      }, files))
    })
    .then(_.flatten)
  // Remove empty lines
    .then(_.filter(line => !_.isEmpty(line)))
  // Parse each line
    .then(_.map(parseAndHandleInvalid))
  // Filter possible nulls from the parse
    .then(_.filter(it => it))
  // Filter only logs that are created after the required timestamp
    .then(_.filter(filter))
  // Sort ascending
    .then(_.sortBy(log => ([log.timestamp, log.serial])))
  // Only send last MAX_LINES lines.
  // Ensures syncing is fast at the expensive of possible
  // gaps in server-side log storage.
    .then(_.takeRight(MAX_LINES))
  // Don't send more than MAX_SIZE
  // to avoid any large payload errors
    .then(_.reduce((acc, val) => {
      const size = JSON.stringify(val).length + DELIMITER_SIZE
      return _.concat(acc, {size: _.getOr(0, 'size', _.last(acc)) + size, val})
    }, []))
    .then(_.takeWhile(r => r.size < MAX_SIZE))
    .then(_.map(_.get('val')))
}

module.exports = { removeLogFiles, queryNewestLogs }
<|endoftext|>


<commit_before>(function(){

  angular
    .module('flexcalendar', [])
    .directive('flexCalendar', flexCalendar);

    function flexCalendar() {

      var template =
      '
'+
        '
'+
          '

'+
          '
{{selectedMonth}} {{selectedYear}}
'+
          '

'+
        '
'+
        '
'+
          '
{{day}}
'+
        '
'+
        '
'+
          '
'+
            '
{{day.day}}
'+
          '
'+
        '
'+
      '
';

      var directive = {
        restrict: 'E',
        scope: {
          options: '=?',
          events: '=?'
        },
        template: template,
        controller: Controller
      };

      return directive;

    }

    Controller.$inject = ['$scope'];

    function Controller($scope) {
      var MONTHS = ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'];
      var WEEKDAYS = ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'];

      $scope.days = [];
      $scope.options = $scope.options || {};
      $scope.options.dayNamesLength = $scope.options.dayNamesLength || 1;

      $scope.onClick = onClick;
      $scope.allowedPrevMonth = allowedPrevMonth;
      $scope.allowedNextMonth = allowedNextMonth;
      $scope.weekDays = weekDays;
      $scope.isDefaultDate = isDefaultDate;
      $scope.prevMonth = prevMonth;
      $scope.nextMonth = nextMonth;

      if ($scope.options.minDate) {
        $scope.options.minDate = new Date($scope.options.minDate);
      }

      if ($scope.options.maxDate) {
        $scope.options.maxDate = new Date($scope.options.maxDate);
      }

      $scope.$watch('options.defaultDate', function() {
        calculateSelectedDate();
      });

      $scope.$watch('options.disabledDates', function() {
        calculateDisabledDates();
      });

      $scope.$watch('events', function() {
        calculateWeeks();
      });

      /////////////////

      function onClick(date, index, domEvent) {
        if (!date || date.disabled) { return; }
        $scope.options.defaultDate = date.date;
        if (date.event) {
          $scope.options.eventClick(date, domEvent);
        } else {
          $scope.options.dateClick(date, domEvent);
        }
      }

      function bindEvent(date) {
        if (!date || !$scope.events) { return; }
        $scope.events.forEach(function(event) {
          event.date = new Date(event.date);
          if (date.year === event.date.getFullYear() && date.month === event.date.getMonth() && date.day === event.date.getDate()) {
            date.event = event;
          }
        });
      }

      function allowedDate(date) {
        if (!$scope.options.minDate && !$scope.options.maxDate) {
          return true;
        }
        var currDate = new Date([date.year, date.month + 1, date.day]);
        if ($scope.options.minDate && (currDate < $scope.options.minDate)) { return false; }
        if ($scope.options.maxDate && (currDate > $scope.options.maxDate)) { return false; }
        return true;
      }

      function disabledDate(date) {
        if (!$scope.options.disabledDates) return false;
        for(var i = 0; i < $scope.options.disabledDates.length; i++){
          if(date.year === $scope.options.disabledDates[i].getFullYear() && date.month === $scope.options.disabledDates[i].getMonth() && date.day === $scope.options.disabledDates[i].getDate()){
            return true;
            break;
          }
        }
      }

      function allowedPrevMonth() {
        var prevYear = null;
        var prevMonth = null;
        if (!$scope.options.minDate) { return true; }
        var currMonth = MONTHS.indexOf($scope.selectedMonth);
        if (currMonth === 0) {
          prevYear = ($scope.selectedYear - 1);
        } else {
          prevYear = $scope.selectedYear;
        }
        if (currMonth === 0) {
          prevMonth = 11;
        } else {
          prevMonth = (currMonth - 1);
        }
        if (prevYear < $scope.options.minDate.getFullYear()) { return false; }
        if (prevYear === $scope.options.minDate.getFullYear()) {
          if (prevMonth < $scope.options.minDate.getMonth()) { return false; }
        }
        return true;
      }

      function allowedNextMonth() {
        var nextYear = null;
        var nextMonth = null;
        if (!$scope.options.maxDate) { return true; }
        var currMonth = MONTHS.indexOf($scope.selectedMonth);
        if (currMonth === 11) {
          nextYear = ($scope.selectedYear + 1);
        } else {
          nextYear = $scope.selectedYear;
        }
        if (currMonth === 11) {
          nextMonth = 0;
        } else {
          nextMonth = (currMonth + 1);
        }
        if (nextYear > $scope.options.maxDate.getFullYear()) { return false; }
        if (nextYear === $scope.options.maxDate.getFullYear()) {
          if (nextMonth > $scope.options.maxDate.getMonth()) { return false; }
        }
        return true;
      }

      function flattenWeek(){
        $scope.days = $scope.weeks.reduce(function(a, b) {
          return a.concat(b);
        });
      }

      function calculateWeeks() {
        $scope.weeks = [];
        var week = null;
        var daysInCurrentMonth = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth) + 1, 0).getDate();

        for (var day = 1; day < daysInCurrentMonth + 1; day += 1) {
          var date = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth), day);
          var dayNumber = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth), day).getDay();
          week = week || [null, null, null, null, null, null, null];
          week[dayNumber] = {
            year: $scope.selectedYear,
            month: MONTHS.indexOf($scope.selectedMonth),
            day: day,
            date: date,
            _month : date.getMonth() + 1,
          };

          if (allowedDate(week[dayNumber])) {
            if ($scope.events) { bindEvent(week[dayNumber]); }
          } else {
            week[dayNumber].disabled = true;
          }

          if (week[dayNumber] && disabledDate(week[dayNumber])) {
            week[dayNumber].disabled = true;
          }

          if (dayNumber === 6 || day === daysInCurrentMonth) {
            $scope.weeks.push(week);
            week = undefined;
          }
        }

        flattenWeek();
      }

      function calculateSelectedDate() {
        if ($scope.options.defaultDate) {
          $scope.options._defaultDate = new Date($scope.options.defaultDate);
        } else {
          $scope.options._defaultDate = new Date();
        }

        $scope.selectedYear  = $scope.options._defaultDate.getFullYear();
        $scope.selectedMonth = MONTHS[$scope.options._defaultDate.getMonth()];
        $scope.selectedDay   = $scope.options._defaultDate.getDate();
        calculateWeeks();
      }

      function calculateDisabledDates() {
        if (!$scope.options.disabledDates || $scope.options.disabledDates.length === 0) return;
        for(var i = 0; i < $scope.options.disabledDates.length; i++){
          $scope.options.disabledDates[i] = new Date($scope.options.disabledDates[i]);
        }
        calculateWeeks();
      }

      function weekDays(size) {
        return WEEKDAYS.map(function(name) { return name.slice(0, size); });
      }

      function isDefaultDate(date) {
        if (!date) { return; }
        var result = date.year === $scope.options._defaultDate.getFullYear() &&
          date.month === $scope.options._defaultDate.getMonth() &&
          date.day === $scope.options._defaultDate.getDate();
        return result;
      }

      function prevMonth() {
        if (!$scope.allowedPrevMonth()) { return; }
        var currIndex = MONTHS.indexOf($scope.selectedMonth);
        if (currIndex === 0) {
          $scope.selectedYear -= 1;
          $scope.selectedMonth = MONTHS[11];
        } else {
          $scope.selectedMonth = MONTHS[currIndex - 1];
        }
        var month = {name: $scope.selectedMonth, index: currIndex - 1, _index: currIndex+2 };
        $scope.options.changeMonth(month);
        calculateWeeks();
      }

      function nextMonth() {
        if (!$scope.allowedNextMonth()) { return; }
        var currIndex = MONTHS.indexOf($scope.selectedMonth);
        if (currIndex === 11) {
          $scope.selectedYear += 1;
          $scope.selectedMonth = MONTHS[0];
        } else {
          $scope.selectedMonth = MONTHS[currIndex + 1];
        }
        var month = {name: $scope.selectedMonth, index: currIndex + 1, _index: currIndex+2 };
        $scope.options.changeMonth(month);
        calculateWeeks();
      }
    }

})();
<commit_msg>fixing reduce to work in safari<commit_after>(function(){

  angular
    .module('flexcalendar', [])
    .directive('flexCalendar', flexCalendar);

    function flexCalendar() {

      var template =
      '
'+
        '
'+
          '

'+
          '
{{selectedMonth}} {{selectedYear}}
'+
          '

'+
        '
'+
        '
'+
          '
{{day}}
'+
        '
'+
        '
'+
          '
'+
            '
{{day.day}}
'+
          '
'+
        '
'+
      '
';

      var directive = {
        restrict: 'E',
        scope: {
          options: '=?',
          events: '=?'
        },
        template: template,
        controller: Controller
      };

      return directive;

    }

    Controller.$inject = ['$scope'];

    function Controller($scope) {
      var MONTHS = ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'];
      var WEEKDAYS = ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'];

      $scope.days = [];
      $scope.options = $scope.options || {};
      $scope.options.dayNamesLength = $scope.options.dayNamesLength || 1;

      $scope.onClick = onClick;
      $scope.allowedPrevMonth = allowedPrevMonth;
      $scope.allowedNextMonth = allowedNextMonth;
      $scope.weekDays = weekDays;
      $scope.isDefaultDate = isDefaultDate;
      $scope.prevMonth = prevMonth;
      $scope.nextMonth = nextMonth;

      if ($scope.options.minDate) {
        $scope.options.minDate = new Date($scope.options.minDate);
      }

      if ($scope.options.maxDate) {
        $scope.options.maxDate = new Date($scope.options.maxDate);
      }

      $scope.$watch('options.defaultDate', function() {
        calculateSelectedDate();
      });

      $scope.$watch('options.disabledDates', function() {
        calculateDisabledDates();
      });

      $scope.$watch('events', function() {
        calculateWeeks();
      });

      /////////////////

      function onClick(date, index, domEvent) {
        if (!date || date.disabled) { return; }
        $scope.options.defaultDate = date.date;
        if (date.event) {
          $scope.options.eventClick(date, domEvent);
        } else {
          $scope.options.dateClick(date, domEvent);
        }
      }

      function bindEvent(date) {
        if (!date || !$scope.events) { return; }
        $scope.events.forEach(function(event) {
          event.date = new Date(event.date);
          if (date.year === event.date.getFullYear() && date.month === event.date.getMonth() && date.day === event.date.getDate()) {
            date.event = event;
          }
        });
      }

      function allowedDate(date) {
        if (!$scope.options.minDate && !$scope.options.maxDate) {
          return true;
        }
        var currDate = new Date([date.year, date.month + 1, date.day]);
        if ($scope.options.minDate && (currDate < $scope.options.minDate)) { return false; }
        if ($scope.options.maxDate && (currDate > $scope.options.maxDate)) { return false; }
        return true;
      }

      function disabledDate(date) {
        if (!$scope.options.disabledDates) return false;
        for(var i = 0; i < $scope.options.disabledDates.length; i++){
          if(date.year === $scope.options.disabledDates[i].getFullYear() && date.month === $scope.options.disabledDates[i].getMonth() && date.day === $scope.options.disabledDates[i].getDate()){
            return true;
            break;
          }
        }
      }

      function allowedPrevMonth() {
        var prevYear = null;
        var prevMonth = null;
        if (!$scope.options.minDate) { return true; }
        var currMonth = MONTHS.indexOf($scope.selectedMonth);
        if (currMonth === 0) {
          prevYear = ($scope.selectedYear - 1);
        } else {
          prevYear = $scope.selectedYear;
        }
        if (currMonth === 0) {
          prevMonth = 11;
        } else {
          prevMonth = (currMonth - 1);
        }
        if (prevYear < $scope.options.minDate.getFullYear()) { return false; }
        if (prevYear === $scope.options.minDate.getFullYear()) {
          if (prevMonth < $scope.options.minDate.getMonth()) { return false; }
        }
        return true;
      }

      function allowedNextMonth() {
        var nextYear = null;
        var nextMonth = null;
        if (!$scope.options.maxDate) { return true; }
        var currMonth = MONTHS.indexOf($scope.selectedMonth);
        if (currMonth === 11) {
          nextYear = ($scope.selectedYear + 1);
        } else {
          nextYear = $scope.selectedYear;
        }
        if (currMonth === 11) {
          nextMonth = 0;
        } else {
          nextMonth = (currMonth + 1);
        }
        if (nextYear > $scope.options.maxDate.getFullYear()) { return false; }
        if (nextYear === $scope.options.maxDate.getFullYear()) {
          if (nextMonth > $scope.options.maxDate.getMonth()) { return false; }
        }
        return true;
      }

      function flattenWeek(){
        $scope.days = $scope.weeks.reduce(function(a, b) {
          return a.concat(b);
        }, "");
      }

      function calculateWeeks() {
        $scope.weeks = [];
        var week = null;
        var daysInCurrentMonth = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth) + 1, 0).getDate();

        for (var day = 1; day < daysInCurrentMonth + 1; day += 1) {
          var date = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth), day);
          var dayNumber = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth), day).getDay();
          week = week || [null, null, null, null, null, null, null];
          week[dayNumber] = {
            year: $scope.selectedYear,
            month: MONTHS.indexOf($scope.selectedMonth),
            day: day,
            date: date,
            _month : date.getMonth() + 1,
          };

          if (allowedDate(week[dayNumber])) {
            if ($scope.events) { bindEvent(week[dayNumber]); }
          } else {
            week[dayNumber].disabled = true;
          }

          if (week[dayNumber] && disabledDate(week[dayNumber])) {
            week[dayNumber].disabled = true;
          }

          if (dayNumber === 6 || day === daysInCurrentMonth) {
            $scope.weeks.push(week);
            week = undefined;
          }
        }

        flattenWeek();
      }

      function calculateSelectedDate() {
        if ($scope.options.defaultDate) {
          $scope.options._defaultDate = new Date($scope.options.defaultDate);
        } else {
          $scope.options._defaultDate = new Date();
        }

        $scope.selectedYear  = $scope.options._defaultDate.getFullYear();
        $scope.selectedMonth = MONTHS[$scope.options._defaultDate.getMonth()];
        $scope.selectedDay   = $scope.options._defaultDate.getDate();
        calculateWeeks();
      }

      function calculateDisabledDates() {
        if (!$scope.options.disabledDates || $scope.options.disabledDates.length === 0) return;
        for(var i = 0; i < $scope.options.disabledDates.length; i++){
          $scope.options.disabledDates[i] = new Date($scope.options.disabledDates[i]);
        }
        calculateWeeks();
      }

      function weekDays(size) {
        return WEEKDAYS.map(function(name) { return name.slice(0, size); });
      }

      function isDefaultDate(date) {
        if (!date) { return; }
        var result = date.year === $scope.options._defaultDate.getFullYear() &&
          date.month === $scope.options._defaultDate.getMonth() &&
          date.day === $scope.options._defaultDate.getDate();
        return result;
      }

      function prevMonth() {
        if (!$scope.allowedPrevMonth()) { return; }
        var currIndex = MONTHS.indexOf($scope.selectedMonth);
        if (currIndex === 0) {
          $scope.selectedYear -= 1;
          $scope.selectedMonth = MONTHS[11];
        } else {
          $scope.selectedMonth = MONTHS[currIndex - 1];
        }
        var month = {name: $scope.selectedMonth, index: currIndex - 1, _index: currIndex+2 };
        $scope.options.changeMonth(month);
        calculateWeeks();
      }

      function nextMonth() {
        if (!$scope.allowedNextMonth()) { return; }
        var currIndex = MONTHS.indexOf($scope.selectedMonth);
        if (currIndex === 11) {
          $scope.selectedYear += 1;
          $scope.selectedMonth = MONTHS[0];
        } else {
          $scope.selectedMonth = MONTHS[currIndex + 1];
        }
        var month = {name: $scope.selectedMonth, index: currIndex + 1, _index: currIndex+2 };
        $scope.options.changeMonth(month);
        calculateWeeks();
      }
    }

})();
<|endoftext|>