<commit_before>/*jslint browser: true, nomen: true */

(function (flow, $, girder) {
    'use strict';

    $(document).ready(function () {
        girder.apiRoot = '/girder/api/v1';
        var app = new flow.App({
            el: 'body'
        });

        // Lookup the ID of the analysis that we wish to perform.
        app.analysisName = "discreteCorrelation-app";
        girder.restRequest({
            path: 'resource/search',
            data: {
                q: app.analysisName,
                types: JSON.stringify(["item"])
            }
        }).done(function (results) {
            app.analysisId = results["item"][0]._id;
            app.readyToAnalyze();
        });

        app.readyToAnalyze = function () {
            if ("column1" in this && "column2" in this && "table" in this && "tree" in this &&
                "analysisId" in this) {
                d3.select("#analyze").classed('disabled', false);
            }
        };

        function toggleInputTablePreview() {
            if ($("#table-preview-icon").hasClass("glyphicon-folder-close")) {
                $("#table-preview-icon").removeClass("glyphicon-folder-close");
                $("#table-preview-icon").addClass("glyphicon-folder-open");
                $("#table-preview-text").text(" Hide input table preview");
            }
            else {
                $("#table-preview-icon").removeClass("glyphicon-folder-open");
                $("#table-preview-icon").addClass("glyphicon-folder-close");
                $("#table-preview-text").text(" Show input table preview");
            }

            $("#input-table-vis").toggle('slow');
        }

        // override upload function for simple mode
        flow.DatasetManagementView.prototype.upload = function (file) {
            var reader = new FileReader();

            reader.onload = _.bind(function (e) {
                var dataset = {
                        name: file.name,
                        data: e.target.result
                    },
                    extension = file.name.split('.'),
                    typeFormat;

                extension = extension[extension.length - 1];
                typeFormat = flow.getTypeFormatsFromExtension(extension)[0];
                typeFormat = {type: typeFormat.type, format: typeFormat.format};
                _.extend(dataset, typeFormat);
                dataset = new Backbone.Model(dataset);

                // modifications for simple app begin here
                // if its a table, get the column names
                if (typeFormat.type == "table") {
                    app.table = dataset.get('data');
                    app.tableFormat = typeFormat.format;
                    d3.select("#table-name").html('Table: ' + file.name + ' ');
                    $("#column-input1").text("Parsing column names...");
                    $("#column-names").empty();
                    flow.retrieveDatasetAsFormat(dataset, "table", "column.names", false, _.bind(function (error, dataset) {
                        var columnNames = dataset.get('data');
                        for (var i = 0; i < columnNames.length; ++i) {
                            // create drag-and-drop elements here
                            $("#column-names").append('
' + columnNames[i] + '
');
                        }
                        $(".draggable").draggable({
                             zIndex: 1, helper: "clone"
                        });
                        d3.select("#column-input1").html('Drag first column of interest here (must be a binary character) ');
                    }, this));

                    flow.retrieveDatasetAsFormat(dataset, "table", "rows", false, _.bind(function (error, dataset) {
                      // show the input table to help the user understand if their data
                      // was parsed correctly or not
                      var rowData = dataset.get('data');
                      rowData.rows = rowData.rows.slice(0, 3);
                      d3.select("#input-table-vis-container").classed('hidden', false);
                      $("#input-table-vis").table({ data: rowData });
                    }, this));

                }

                else if (typeFormat.type == "tree") {
                    app.tree = dataset.get('data');
                    d3.select("#tree-name").html('Tree: ' + file.name + ' ');
                }
                app.readyToAnalyze();

                this.datasets.off('add', null, 'set-collection').add(dataset);
            }, this);

            reader.readAsText(file);
        };

        $("#column-input1").droppable({
            drop: function( event, ui ) {
                var COI = ui.draggable.text();
                app.column1 = COI;
                d3.select("#column-input1")
                    .classed('btn-primary', true)
                    .classed('btn-success', false)
                    .classed('bg-warning', false)
                    .html(COI + ' ');
                app.readyToAnalyze();
            },
            over: function (event, ui) {
                d3.select("#column-input1")
                    .classed('btn-success', true)
                    .classed('bg-warning', false);
            },
            out: function (event, ui) {
                d3.select("#column-input1")
                    .classed('btn-success', false)
                    .classed('bg-warning', true);
            }
            });

            $("#column-input2").droppable({
                drop: function( event, ui ) {
                    var COI = ui.draggable.text();
                    app.column2 = COI;
                    d3.select("#column-input2")
                        .classed('btn-primary', true)
                        .classed('btn-success', false)
                        .classed('bg-warning', false)
                        .html(COI + ' ');
                    app.readyToAnalyze();
                },
                over: function (event, ui) {
                    d3.select("#column-input2")
                        .classed('btn-success', true)
                        .classed('bg-warning', false);
                },
                out: function (event, ui) {
                    d3.select("#column-input2")
                        .classed('btn-success', false)
                        .classed('bg-warning', true);
                }
                });
        $("#analyze").click(function() {
            $("#analyze").attr("disabled", "disabled");
            $("#analyze").text("Re-run");
            $("#notice").text("Performing analysis...");

            var inputs = {
                table:  {type: "table",  format: app.tableFormat,    data: app.table},
                tree:   {type: "tree",   format: "newick",           data: app.tree},
                column: {type: "string", format: "text",             data: app.column1},
                column: {type: "string", format: "text",             data: app.column2},
            };

            var outputs = {
                result: {type: "table", format: "rows"}
            };

            flow.performAnalysis(app.analysisId, inputs, outputs,
                _.bind(function (error, result) {
                    app.taskId = result._id;
                    setTimeout(_.bind(app.checkResult, app), 1000);
                }, app));

        app.checkResult = function () {
            var check_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/status'
            girder.restRequest({path: check_url}).done(_.bind(function (result) {
                console.log(result.status);
                if (result.status === 'SUCCESS') {
                    // get result data
                    var result_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/result'
                    girder.restRequest({path: result_url}).done(_.bind(function (data) {
                        app.result = data.result.result.data;

						console.log(app.result.rows[0])

                        // render results
						$("#result").append("
Results:<\h2>");
						$("#result").append("Column analyzed: ", app.column, "
");
						$("#result").append("Analysis type: ", app.analysisType, "
");
						$("#result").append("Estimated test statistic: ");
                        if(app.analysisType=="discrete lambda" | app.analysisType=="continuous lambda") {
							$("#result").append("lambda = ", app.result.rows[0][app.column + ".lambdaValue"].toFixed(2), "

")
						} else {
							$("#result").append("K = ", app.result.rows[0][app.column + ".KValue"].toFixed(2), "

")
						}

                       if(app.analysisType=="discrete lambda" | app.analysisType=="continuous lambda") {
							$("#result").append("Statistical test: likelihood ratio
")
							$("#result").append("     lnL of the null model (lambda = 0): ", app.result.rows[0][app.column + ".lnlValues.Lambda fixed at zero"].toFixed(2), "
")
							$("#result").append("     lnL of the alternative model (lambda estimated): ", app.result.rows[0][app.column + ".lnlValues.Lambda estimated"].toFixed(2), "
")
							$("#result").append("     Chi-squared test statistic: ", app.result.rows[0][app.column + ".chisqTestStat"].toFixed(2), "
")
							$("#result").append("     P-value: ", app.result.rows[0][app.column + ".chisqPVal"].toFixed(3), "
")

							if(app.result.rows[0][app.column + ".chisqPVal"] < 0.05) {
								$("#result").append("

Conclusion:  Reject the null hypothesis of no phylogenetic signal.
")
							} else {
								$("#result").append("

Conclusion:  Fail to reject the null hypothesis of no phylogenetic signal.
")
							}

						} else {
							$("#result").append("xxx")
						}

                        $("#analyze").removeAttr("disabled");
                        $("#notice").text("Analysis succeeded!");

                    }, this));

                } else if (result.status === 'FAILURE') {
                    $("#analyze").removeAttr("disabled");
                    $("#notice").text("Analysis failed. " + result.message);
                } else {
                    setTimeout(_.bind(this.checkResult, this), 1000);
                }
            }, this));
        };

        });

        $("#help").click(function() {
            $("#upload").popover({
                title: 'Step #1',
                content: 'Upload your table (csv or tsv) and tree (newick) here',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#upload").popover('toggle');
            $("#column-input1").popover({
                title: 'Step #2',
                content: 'Drag your column of interest here',
                placement: 'left',
                trigger: 'manual'
            });
            $("#column-input1").popover('toggle');
            $("#analyze").popover({
                title: 'Step #3',
                content: 'Click on the "Go!" button',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#analyze").popover('toggle');

        });

        $("#table-preview").click(function() {
            toggleInputTablePreview();
        });

        app.render();
    });
}(window.flow, window.$, window.girder));
<commit_msg>closer<commit_after>/*jslint browser: true, nomen: true */

(function (flow, $, girder) {
    'use strict';

    $(document).ready(function () {
        girder.apiRoot = '/girder/api/v1';
        var app = new flow.App({
            el: 'body'
        });

        // Lookup the ID of the analysis that we wish to perform.
        app.analysisName = "discreteCorrelation-app";
        girder.restRequest({
            path: 'resource/search',
            data: {
                q: app.analysisName,
                types: JSON.stringify(["item"])
            }
        }).done(function (results) {
            app.analysisId = results["item"][0]._id;
            app.readyToAnalyze();
        });

        app.readyToAnalyze = function () {
            if ("column1" in this && "column2" in this && "table" in this && "tree" in this &&
                "analysisId" in this) {
                d3.select("#analyze").classed('disabled', false);
            }
        };

        function toggleInputTablePreview() {
            if ($("#table-preview-icon").hasClass("glyphicon-folder-close")) {
                $("#table-preview-icon").removeClass("glyphicon-folder-close");
                $("#table-preview-icon").addClass("glyphicon-folder-open");
                $("#table-preview-text").text(" Hide input table preview");
            }
            else {
                $("#table-preview-icon").removeClass("glyphicon-folder-open");
                $("#table-preview-icon").addClass("glyphicon-folder-close");
                $("#table-preview-text").text(" Show input table preview");
            }

            $("#input-table-vis").toggle('slow');
        }

        // override upload function for simple mode
        flow.DatasetManagementView.prototype.upload = function (file) {
            var reader = new FileReader();

            reader.onload = _.bind(function (e) {
                var dataset = {
                        name: file.name,
                        data: e.target.result
                    },
                    extension = file.name.split('.'),
                    typeFormat;

                extension = extension[extension.length - 1];
                typeFormat = flow.getTypeFormatsFromExtension(extension)[0];
                typeFormat = {type: typeFormat.type, format: typeFormat.format};
                _.extend(dataset, typeFormat);
                dataset = new Backbone.Model(dataset);

                // modifications for simple app begin here
                // if its a table, get the column names
                if (typeFormat.type == "table") {
                    app.table = dataset.get('data');
                    app.tableFormat = typeFormat.format;
                    d3.select("#table-name").html('Table: ' + file.name + ' ');
                    $("#column-input1").text("Parsing column names...");
                    $("#column-names").empty();
                    flow.retrieveDatasetAsFormat(dataset, "table", "column.names", false, _.bind(function (error, dataset) {
                        var columnNames = dataset.get('data');
                        for (var i = 0; i < columnNames.length; ++i) {
                            // create drag-and-drop elements here
                            $("#column-names").append('
' + columnNames[i] + '
');
                        }
                        $(".draggable").draggable({
                             zIndex: 1, helper: "clone"
                        });
                        d3.select("#column-input1").html('Drag first column of interest here (must be a binary character) ');
                    }, this));

                    flow.retrieveDatasetAsFormat(dataset, "table", "rows", false, _.bind(function (error, dataset) {
                      // show the input table to help the user understand if their data
                      // was parsed correctly or not
                      var rowData = dataset.get('data');
                      rowData.rows = rowData.rows.slice(0, 3);
                      d3.select("#input-table-vis-container").classed('hidden', false);
                      $("#input-table-vis").table({ data: rowData });
                    }, this));

                }

                else if (typeFormat.type == "tree") {
                    app.tree = dataset.get('data');
                    d3.select("#tree-name").html('Tree: ' + file.name + ' ');
                }
                app.readyToAnalyze();

                this.datasets.off('add', null, 'set-collection').add(dataset);
            }, this);

            reader.readAsText(file);
        };

        $("#column-input1").droppable({
            drop: function( event, ui ) {
                var COI = ui.draggable.text();
                app.column1 = COI;
                d3.select("#column-input1")
                    .classed('btn-primary', true)
                    .classed('btn-success', false)
                    .classed('bg-warning', false)
                    .html(COI + ' ');
                d3.select("#column-input2")
                        .html('Now place second variable of interest here...');

                app.readyToAnalyze();
            },
            over: function (event, ui) {
                d3.select("#column-input1")
                    .classed('btn-success', true)
                    .classed('bg-warning', false);
            },
            out: function (event, ui) {
                d3.select("#column-input1")
                    .classed('btn-success', false)
                    .classed('bg-warning', true);
            }
            });

            $("#column-input2").droppable({
                drop: function( event, ui ) {
                    var COI = ui.draggable.text();
                    app.column2 = COI;
                    d3.select("#column-input2")
                        .classed('btn-primary', true)
                        .classed('btn-success', false)
                        .classed('bg-warning', false)
                        .html(COI + ' ');
                    app.readyToAnalyze();
                },
                over: function (event, ui) {
                    d3.select("#column-input2")
                        .classed('btn-success', true)
                        .classed('bg-warning', false);
                },
                out: function (event, ui) {
                    d3.select("#column-input2")
                        .classed('btn-success', false)
                        .classed('bg-warning', true);
                }
                });
        $("#analyze").click(function() {
            $("#analyze").attr("disabled", "disabled");
            $("#analyze").text("Re-run");
            $("#notice").text("Performing analysis...");

            var inputs = {
                table:  {type: "table",  format: app.tableFormat,    data: app.table},
                tree:   {type: "tree",   format: "newick",           data: app.tree},
                column1: {type: "string", format: "text",             data: app.column1},
                column2: {type: "string", format: "text",             data: app.column2},
            };

            var outputs = {
                result: {type: "table", format: "rows"}
            };

            flow.performAnalysis(app.analysisId, inputs, outputs,
                _.bind(function (error, result) {
                    app.taskId = result._id;
                    setTimeout(_.bind(app.checkResult, app), 1000);
                }, app));

        app.checkResult = function () {
            var check_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/status'
            girder.restRequest({path: check_url}).done(_.bind(function (result) {
                console.log(result.status);
                if (result.status === 'SUCCESS') {
                    // get result data
                    var result_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/result'
                    girder.restRequest({path: result_url}).done(_.bind(function (data) {
                        app.result = data.result.result.data;

						console.log(app.result.rows[0])

                        // render results
						$("#result").append("
Results:<\h2>");
						$("#result").append("Column analyzed: ", app.column, "
");
						$("#result").append("Analysis type: ", app.analysisType, "
");
						$("#result").append("Estimated test statistic: ");
                        if(app.analysisType=="discrete lambda" | app.analysisType=="continuous lambda") {
							$("#result").append("lambda = ", app.result.rows[0][app.column + ".lambdaValue"].toFixed(2), "

")
						} else {
							$("#result").append("K = ", app.result.rows[0][app.column + ".KValue"].toFixed(2), "

")
						}

                       if(app.analysisType=="discrete lambda" | app.analysisType=="continuous lambda") {
							$("#result").append("Statistical test: likelihood ratio
")
							$("#result").append("     lnL of the null model (lambda = 0): ", app.result.rows[0][app.column + ".lnlValues.Lambda fixed at zero"].toFixed(2), "
")
							$("#result").append("     lnL of the alternative model (lambda estimated): ", app.result.rows[0][app.column + ".lnlValues.Lambda estimated"].toFixed(2), "
")
							$("#result").append("     Chi-squared test statistic: ", app.result.rows[0][app.column + ".chisqTestStat"].toFixed(2), "
")
							$("#result").append("     P-value: ", app.result.rows[0][app.column + ".chisqPVal"].toFixed(3), "
")

							if(app.result.rows[0][app.column + ".chisqPVal"] < 0.05) {
								$("#result").append("

Conclusion:  Reject the null hypothesis of no phylogenetic signal.
")
							} else {
								$("#result").append("

Conclusion:  Fail to reject the null hypothesis of no phylogenetic signal.
")
							}

						} else {
							$("#result").append("xxx")
						}

                        $("#analyze").removeAttr("disabled");
                        $("#notice").text("Analysis succeeded!");

                    }, this));

                } else if (result.status === 'FAILURE') {
                    $("#analyze").removeAttr("disabled");
                    $("#notice").text("Analysis failed. " + result.message);
                } else {
                    setTimeout(_.bind(this.checkResult, this), 1000);
                }
            }, this));
        };

        });

        $("#help").click(function() {
            $("#upload").popover({
                title: 'Step #1',
                content: 'Upload your table (csv or tsv) and tree (newick) here',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#upload").popover('toggle');
            $("#column-input1").popover({
                title: 'Step #2',
                content: 'Drag your column of interest here',
                placement: 'left',
                trigger: 'manual'
            });
            $("#column-input1").popover('toggle');
            $("#analyze").popover({
                title: 'Step #3',
                content: 'Click on the "Go!" button',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#analyze").popover('toggle');

        });

        $("#table-preview").click(function() {
            toggleInputTablePreview();
        });

        app.render();
    });
}(window.flow, window.$, window.girder));
<|endoftext|>


<commit_before>// @flow
import { Component, createElement, PropTypes } from 'react'

import validAttr from '../utils/validAttr'
import { CHANNEL } from './ThemeProvider'

import type { RuleSet, Target } from '../types'

/* eslint-disable react/prefer-stateless-function */
class AbstractStyledComponent extends Component {
  static isPrototypeOf: Function
  state: any
}

export default (ComponentStyle: any) => {
  const createStyledComponent = (target: Target, rules: RuleSet, parent?: Target) => {
    /* Handle styled(OtherStyledComponent) differently */
    const isStyledComponent = AbstractStyledComponent.isPrototypeOf(target)
    if (isStyledComponent) {
      return createStyledComponent(target.target, target.rules.concat(rules), target)
    }

    const isTag = typeof target === 'string'
    const componentStyle = new ComponentStyle(rules)

    class StyledComponent extends AbstractStyledComponent {
      static rules: RuleSet
      static target: Target
      state: {
        theme: any,
      }
      unsubscribe: Function

      constructor() {
        super()
        this.state = {
          theme: null,
        }
      }

      componentWillMount() {
        // If there is a theme in the context, subscribe to the event emitter. This
        // is necessary due to pure components blocking context updates, this circumvents
        // that by updating when an event is emitted
        if (this.context[CHANNEL]) {
          const subscribe = this.context[CHANNEL]
          this.unsubscribe = subscribe(theme => {
            // This will be called once immediately
            this.setState({ theme })
          })
        }
      }

      componentWillUnmount() {
        if (this.unsubscribe) {
          this.unsubscribe()
        }
      }

      /* eslint-disable react/prop-types */
      render() {
        const { className, children } = this.props
        const theme = this.state.theme || {}
        const executionContext = Object.assign({}, this.props, { theme })

        const generatedClassName = componentStyle.generateAndInjectStyles(executionContext)
        const propsForElement = {}
        /* Don't pass through non HTML tags through to HTML elements */
        Object.keys(this.props)
          .filter(propName => !isTag || validAttr(propName))
          .forEach(propName => {
            propsForElement[propName] = this.props[propName]
          })
        propsForElement.className = [className, generatedClassName].filter(x => x).join(' ')

        return createElement(target, propsForElement, children)
      }
    }

    /* Used for inheritance */
    StyledComponent.rules = rules
    StyledComponent.target = target
    StyledComponent.defaultProps = parent && parent.defaultProps

    StyledComponent.displayName = isTag ? `styled.${target}` : `Styled(${target.displayName})`
    StyledComponent.contextTypes = {
      [CHANNEL]: PropTypes.func,
    }
    return StyledComponent
  }

  return createStyledComponent
}
<commit_msg>Fix extending styled component<commit_after>// @flow
import { Component, createElement, PropTypes } from 'react'

import validAttr from '../utils/validAttr'
import { CHANNEL } from './ThemeProvider'

import type { RuleSet, Target } from '../types'

/* eslint-disable react/prefer-stateless-function */
class AbstractStyledComponent extends Component {
  static isPrototypeOf: Function
  state: any
}

export default (ComponentStyle: any) => {
  const createStyledComponent = (target: Target, rules: RuleSet, parent?: Target) => {
    /* Handle styled(OtherStyledComponent) differently */
    const isStyledComponent = AbstractStyledComponent.isPrototypeOf(target)
    if (isStyledComponent) {
      return createStyledComponent(target.target, target.rules.concat(rules), target)
    }

    const isTag = typeof target === 'string'
    const componentStyle = new ComponentStyle(rules)
    const ParentComponent = parent || AbstractStyledComponent

    class StyledComponent extends ParentComponent {
      static rules: RuleSet
      static target: Target
      state: {
        theme: any,
      }
      unsubscribe: Function

      constructor() {
        super()
        this.state = {
          theme: null,
        }
      }

      componentWillMount() {
        // If there is a theme in the context, subscribe to the event emitter. This
        // is necessary due to pure components blocking context updates, this circumvents
        // that by updating when an event is emitted
        if (this.context[CHANNEL]) {
          const subscribe = this.context[CHANNEL]
          this.unsubscribe = subscribe(theme => {
            // This will be called once immediately
            this.setState({ theme })
          })
        }
      }

      componentWillUnmount() {
        if (this.unsubscribe) {
          this.unsubscribe()
        }
      }

      /* eslint-disable react/prop-types */
      render() {
        const { className, children } = this.props
        const theme = this.state.theme || {}
        const executionContext = Object.assign({}, this.props, { theme })

        const generatedClassName = componentStyle.generateAndInjectStyles(executionContext)
        const propsForElement = {}
        /* Don't pass through non HTML tags through to HTML elements */
        Object.keys(this.props)
          .filter(propName => !isTag || validAttr(propName))
          .forEach(propName => {
            propsForElement[propName] = this.props[propName]
          })
        propsForElement.className = [className, generatedClassName].filter(x => x).join(' ')

        return createElement(target, propsForElement, children)
      }
    }

    /* Used for inheritance */
    StyledComponent.rules = rules
    StyledComponent.target = target

    StyledComponent.displayName = isTag ? `styled.${target}` : `Styled(${target.displayName})`
    StyledComponent.contextTypes = {
      [CHANNEL]: PropTypes.func,
    }
    return StyledComponent
  }

  return createStyledComponent
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, localStorage, brackets */
/*unittests: Preferences Base */

/**
 * Base infrastructure for managing preferences.
 *
 */
define(function (require, exports, module) {
    "use strict";

    var FileUtils         = require("file/FileUtils"),
        NativeFileSystem  = require("file/NativeFileSystem").NativeFileSystem,
        ExtensionLoader   = require("utils/ExtensionLoader"),
        CollectionUtils   = require("utils/CollectionUtils"),
        _                 = require("lodash"),
        Async             = require("utils/Async"),
        globmatch         = require("thirdparty/globmatch");

    var PREFERENCE_CHANGE = "preferenceChange";

    function MemoryStorage(data) {
        this.data = data || {};
    }

    MemoryStorage.prototype = {
        load: function () {
            var result = $.Deferred();
            result.resolve(this.data);
            return result.promise();
        },

        save: function (newData) {
            var result = $.Deferred();
            this.data = newData;
            result.resolve();
            return result.promise();
        }
    };

    function FileStorage(path, createIfNew) {
        this.path = path;
        this.createIfNew = createIfNew;
    }

    function ParsingError(message) {
        this.name = "ParsingError";
        this.message = message || "";
    }

    ParsingError.prototype = Error.prototype;

    FileStorage.prototype = {
        load: function () {
            var result = $.Deferred();
            var path = this.path;
            var createIfNew = this.createIfNew;

            brackets.fs.readFile(path, NativeFileSystem._FSEncodings.UTF8, function (err, text) {
                if (err) {
                    if (createIfNew) {
                        result.resolve({});
                    } else {
                        result.reject(new Error("Unable to load prefs at " + path + " " + err));
                    }
                    return;
                }
                try {
                    result.resolve(JSON.parse(text));
                } catch (e) {
                    result.reject(new ParsingError("Invalid JSON settings at " + path + "(" + e.toString() + ")"));
                }
            });

            return result.promise();
        },

        save: function (newData) {
            var result = $.Deferred();
            var path = this.path;
            try {
                brackets.fs.writeFile(path, JSON.stringify(newData, null, 4), NativeFileSystem._FSEncodings.UTF8, function (err) {
                    if (err) {
                        result.reject("Unable to save prefs at " + path + " " + err);
                    } else {
                        result.resolve();
                    }
                });
            } catch (e) {
                result.reject("Unable to convert prefs to JSON" + e.toString());
            }
            return result.promise();
        }
    };

    function Scope(storage) {
        this.storage = storage;
        this.data = undefined;
        this._dirty = false;
    }

    Scope.prototype = {
        load: function () {
            var result = $.Deferred();
            this.storage.load()
                .then(function (data) {
                    this.data = data;
                    result.resolve();
                }.bind(this))
                .fail(function (error) {
                    result.reject(error);
                });
            return result.promise();
        },

        save: function () {
            if (this._dirty) {
                return this.storage.save(this.data);
            } else {
                return $.Deferred().resolve().promise();
            }
        },

        setValue: function (id, value) {
            this._dirty = true;
            this.data[id] = value;
        },

        getValue: function (id, layers) {
            var layerCounter;
            for (layerCounter = 0; layerCounter < layers.length; layerCounter++) {
                var result = layers[layerCounter](this.data, id);
                if (result !== undefined) {
                    return result;
                }
            }
            return this.data[id];
        },

        getKeys: function (layers) {
            var data = this.data,
                keys = _.keys(data);

            layers.forEach(function (layer) {
                keys = layer(data, keys);
            });
            return keys;
        }
    };

    function LanguageLayer() {
    }

    LanguageLayer.prototype = {
        setLanguage: function (languageID) {
            $(this).trigger("beforeLayerChange");
            this.language = languageID;
            $(this).trigger("afterLayerChange");
        },

        getValue: function (data, id) {
            var language = this.language;
            if (data.language && data.language[language]) {
                return data.language[language][id];
            }
        },

        getKeys: function (data, currentKeyList) {
            var language = this.language;
            currentKeyList = _.without(currentKeyList, "language");
            if (data.language && data.language[language]) {
                var languageKeys = Object.keys(data.language[language]);
                return _.union(currentKeyList, languageKeys);
            }
            return currentKeyList;
        }
    };

    function PathLayer() {
    }

    PathLayer.prototype = {
        setFilename: function (filename) {
            $(this).trigger("beforeLayerChange");
            this.filename = filename;
            $(this).trigger("afterLayerChange");
        },

        _findMatchingGlob: function (path) {
            var globs = Object.keys(path),
                filename = this.filename,
                globCounter;

            if (!filename) {
                return undefined;
            }

            for (globCounter = 0; globCounter < globs.length; globCounter++) {
                var glob = globs[globCounter];

                if (globmatch(filename, glob)) {
                    return glob;
                }
            }
        },

        getValue: function (data, id) {
            var path = data.path;
            if (path) {
                var glob = this._findMatchingGlob(path);
                if (glob) {
                    if (path[glob][id]) {
                        return path[glob][id];
                    } else {
                        return undefined;
                    }
                }
            }
            return undefined;
        },

        getKeys: function (data, currentKeyList) {
            var path = data.path;
            currentKeyList = _.without(currentKeyList, "path");
            if (path) {
                var glob = this._findMatchingGlob(data.path);
                if (glob) {
                    var pathKeys = Object.keys(data.path[glob]);
                    return _.union(currentKeyList, pathKeys);
                }
            }
            return currentKeyList;
        }
    };

    function PreferencesManager() {
        this._knownPrefs = {};
        this._scopes = {
            "default": new Scope(new MemoryStorage())
        };

        // Memory-based scope loads synchronously
        this._scopes["default"].load();
        this._scopeOrder = ["default"];
        this._pendingScopes = {};

        this._layers = {};
        this._layerGetters = [];
        this._layerKeys = [];

        this._saveInProgress = false;
        this._nextSaveDeferred = null;
    }

    PreferencesManager.prototype = {
        definePreference: function (id, type, initial, options) {
            options = options || {};
            if (this._knownPrefs.hasOwnProperty(id)) {
                throw new Error("Preference " + id + " was redefined");
            }
            this._knownPrefs[id] = {
                type: type,
                initial: initial,
                name: options.name,
                description: options.description
            };
            this.setValue("default", id, initial);
        },

        addToScopeOrder: function (id, addBefore) {
            if (!addBefore) {
                this._scopeOrder.unshift(id);
            } else {
                var addIndex = this._scopeOrder.indexOf(addBefore);
                if (addIndex > -1) {
                    this._scopeOrder.splice(addIndex, 0, id);
                } else {
                    var queue = this._pendingScopes[addBefore];
                    if (!queue) {
                        queue = [];
                        this._pendingScopes[addBefore] = queue;
                    }
                    queue.unshift(id);
                }
            }
            if (this._pendingScopes[id]) {
                var pending = this._pendingScopes[id];
                delete this._pendingScopes[id];
                pending.forEach(function (scopeID) {
                    this.addToScopeOrder(scopeID, id);
                }.bind(this));
            }
        },

        addScope: function (id, scope, addBefore) {
            if (this._scopes[id]) {
                throw new Error("Attempt to redefine preferences scope: " + id);
            }

            // Check to see if "scope" might be a Storage instead
            if (!scope.getValue) {
                scope = new Scope(scope);
            }

            var deferred = $.Deferred();

            scope.load()
                .then(function () {
                    this._inspectChangesAndFire(function () {
                        this._scopes[id] = scope;
                        this.addToScopeOrder(id, addBefore);
                    }.bind(this));
                    deferred.resolve(id, scope);
                }.bind(this))
                .fail(function (err) {
                    // With preferences, it is valid for there to be no file.
                    // It is not valid to have an unparseable file.
                    if (err instanceof ParsingError) {
                        console.error(err);
                    }
                });

            return deferred.promise();
        },

        removeScope: function (id) {
            this._inspectChangesAndFire(function () {
                delete this._scopes[id];
                var scopeIndex = this._scopeOrder.indexOf(id);
                if (scopeIndex > -1) {
                    this._scopeOrder.splice(scopeIndex, 1);
                }
            }.bind(this));
        },

        _getAllKeysAndPrefs: function (extraKeys) {
            var layerKeys = this._layerKeys;

            var keys = _.map(this._scopes, function (scope) {
                return scope.getKeys(layerKeys);
            });

            keys = _.union.apply(null, keys);
            if (extraKeys) {
                keys = _.union(keys, extraKeys);
            }

            var prefs = _.object(keys, _.map(keys, function (key) {
                return this.getValue(key);
            }.bind(this)));

            return {
                keys: keys,
                prefs: prefs
            };
        },

        _inspectChangesAndFire: function (operation) {
            var before = this._getAllKeysAndPrefs();

            operation();

            var after = this._getAllKeysAndPrefs(before.keys);

            this._fireChanges(before, after);
        },

        _fireChanges: function (before, after) {
            var differentProps = _.map(after.keys, function (key) {
                var beforeValue = before.prefs[key],
                    afterValue = after.prefs[key];

                if (afterValue !== beforeValue) {
                    return {
                        id: key,
                        oldValue: beforeValue,
                        newValue: afterValue
                    };
                }
                return undefined;
            });

            var self = $(this);
            differentProps.forEach(function (data) {
                if (data !== undefined) {
                    self.trigger("preferenceChange", data);
                }
            });

        },

        addLayer: function (id, layer) {
            if (this._layers[id]) {
                throw new Error("Attempt to redefine preferences layer: " + id);
            }

            this._inspectChangesAndFire(function () {
                this._layers[id] = layer;
                this._layerGetters.push(layer.getValue.bind(layer));
                this._layerKeys.push(layer.getKeys.bind(layer));
            }.bind(this));

            $(layer).on("beforeLayerChange", this._beforeLayerChange.bind(this));
            $(layer).on("afterLayerChange", this._afterLayerChange.bind(this));
        },

        _beforeLayerChange: function () {
            this._preLayerChangeData = this._getAllKeysAndPrefs();
        },

        _afterLayerChange: function () {
            if (!this._preLayerChangeData) {
                console.error("After layer change fired without before layer change. This should not happen.");
                return;
            }

            var before = this._preLayerChangeData;
            delete this._preLayerChangeData;

            var after = this._getAllKeysAndPrefs(before.keys);

            this._fireChanges(before, after);
        },

        setValue: function (scopeName, id, value) {
            var scope = this._scopes[scopeName];
            if (!scope) {
                throw new Error("Attempt to set preference in non-existent scope: " + scopeName);
            }

            var oldValue = this.getValue(id);
            scope.setValue(id, value);

            var newValue = this.getValue(id);

            if (oldValue !== newValue) {
                $(this).trigger(PREFERENCE_CHANGE, {
                    id: id,
                    newValue: newValue,
                    oldValue: oldValue
                });
            }
        },

        getValue: function (id) {
            var scopeCounter,
                scopeOrder = this._scopeOrder,
                layerGetters = this._layerGetters;

            for (scopeCounter = 0; scopeCounter < scopeOrder.length; scopeCounter++) {
                var scope = this._scopes[scopeOrder[scopeCounter]];
                var result = scope.getValue(id, layerGetters);
                if (result !== undefined) {
                    return result;
                }
            }
        },

        save: function () {
            if (this._saveInProgress) {
                if (!this._nextSaveDeferred) {
                    this._nextSaveDeferred = $.Deferred();
                }
                return this._nextSaveDeferred.promise();
            }

            this._saveInProgress = true;
            var deferred = this._nextSaveDeferred || $.Deferred();
            this._nextSaveDeferred = null;

            Async.doInParallel(this._scopeOrder, function (id) {
                var scope = this._scopes[id];
                return scope.save();
            }.bind(this))
                .then(function () {
                    this._saveInProgress = false;
                    if (this._nextSaveDeferred) {
                        this.save();
                    }
                    deferred.resolve();
                }.bind(this))
                .fail(function (err) {
                    deferred.reject(err);
                });

            return deferred.promise();
        }
    };

    exports.PreferencesManager = PreferencesManager;
    exports.Scope = Scope;
    exports.MemoryStorage = MemoryStorage;
    exports.LanguageLayer = LanguageLayer;
    exports.PathLayer = PathLayer;
    exports.FileStorage = FileStorage;
});<commit_msg>Revise lodash reference<commit_after>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, localStorage, brackets */
/*unittests: Preferences Base */

/**
 * Base infrastructure for managing preferences.
 *
 */
define(function (require, exports, module) {
    "use strict";

    var FileUtils         = require("file/FileUtils"),
        NativeFileSystem  = require("file/NativeFileSystem").NativeFileSystem,
        ExtensionLoader   = require("utils/ExtensionLoader"),
        CollectionUtils   = require("utils/CollectionUtils"),
        _                 = require("thirdparty/lodash"),
        Async             = require("utils/Async"),
        globmatch         = require("thirdparty/globmatch");

    var PREFERENCE_CHANGE = "preferenceChange";

    function MemoryStorage(data) {
        this.data = data || {};
    }

    MemoryStorage.prototype = {
        load: function () {
            var result = $.Deferred();
            result.resolve(this.data);
            return result.promise();
        },

        save: function (newData) {
            var result = $.Deferred();
            this.data = newData;
            result.resolve();
            return result.promise();
        }
    };

    function FileStorage(path, createIfNew) {
        this.path = path;
        this.createIfNew = createIfNew;
    }

    function ParsingError(message) {
        this.name = "ParsingError";
        this.message = message || "";
    }

    ParsingError.prototype = Error.prototype;

    FileStorage.prototype = {
        load: function () {
            var result = $.Deferred();
            var path = this.path;
            var createIfNew = this.createIfNew;

            brackets.fs.readFile(path, NativeFileSystem._FSEncodings.UTF8, function (err, text) {
                if (err) {
                    if (createIfNew) {
                        result.resolve({});
                    } else {
                        result.reject(new Error("Unable to load prefs at " + path + " " + err));
                    }
                    return;
                }
                try {
                    result.resolve(JSON.parse(text));
                } catch (e) {
                    result.reject(new ParsingError("Invalid JSON settings at " + path + "(" + e.toString() + ")"));
                }
            });

            return result.promise();
        },

        save: function (newData) {
            var result = $.Deferred();
            var path = this.path;
            try {
                brackets.fs.writeFile(path, JSON.stringify(newData, null, 4), NativeFileSystem._FSEncodings.UTF8, function (err) {
                    if (err) {
                        result.reject("Unable to save prefs at " + path + " " + err);
                    } else {
                        result.resolve();
                    }
                });
            } catch (e) {
                result.reject("Unable to convert prefs to JSON" + e.toString());
            }
            return result.promise();
        }
    };

    function Scope(storage) {
        this.storage = storage;
        this.data = undefined;
        this._dirty = false;
    }

    Scope.prototype = {
        load: function () {
            var result = $.Deferred();
            this.storage.load()
                .then(function (data) {
                    this.data = data;
                    result.resolve();
                }.bind(this))
                .fail(function (error) {
                    result.reject(error);
                });
            return result.promise();
        },

        save: function () {
            if (this._dirty) {
                return this.storage.save(this.data);
            } else {
                return $.Deferred().resolve().promise();
            }
        },

        setValue: function (id, value) {
            this._dirty = true;
            this.data[id] = value;
        },

        getValue: function (id, layers) {
            var layerCounter;
            for (layerCounter = 0; layerCounter < layers.length; layerCounter++) {
                var result = layers[layerCounter](this.data, id);
                if (result !== undefined) {
                    return result;
                }
            }
            return this.data[id];
        },

        getKeys: function (layers) {
            var data = this.data,
                keys = _.keys(data);

            layers.forEach(function (layer) {
                keys = layer(data, keys);
            });
            return keys;
        }
    };

    function LanguageLayer() {
    }

    LanguageLayer.prototype = {
        setLanguage: function (languageID) {
            $(this).trigger("beforeLayerChange");
            this.language = languageID;
            $(this).trigger("afterLayerChange");
        },

        getValue: function (data, id) {
            var language = this.language;
            if (data.language && data.language[language]) {
                return data.language[language][id];
            }
        },

        getKeys: function (data, currentKeyList) {
            var language = this.language;
            currentKeyList = _.without(currentKeyList, "language");
            if (data.language && data.language[language]) {
                var languageKeys = Object.keys(data.language[language]);
                return _.union(currentKeyList, languageKeys);
            }
            return currentKeyList;
        }
    };

    function PathLayer() {
    }

    PathLayer.prototype = {
        setFilename: function (filename) {
            $(this).trigger("beforeLayerChange");
            this.filename = filename;
            $(this).trigger("afterLayerChange");
        },

        _findMatchingGlob: function (path) {
            var globs = Object.keys(path),
                filename = this.filename,
                globCounter;

            if (!filename) {
                return undefined;
            }

            for (globCounter = 0; globCounter < globs.length; globCounter++) {
                var glob = globs[globCounter];

                if (globmatch(filename, glob)) {
                    return glob;
                }
            }
        },

        getValue: function (data, id) {
            var path = data.path;
            if (path) {
                var glob = this._findMatchingGlob(path);
                if (glob) {
                    if (path[glob][id]) {
                        return path[glob][id];
                    } else {
                        return undefined;
                    }
                }
            }
            return undefined;
        },

        getKeys: function (data, currentKeyList) {
            var path = data.path;
            currentKeyList = _.without(currentKeyList, "path");
            if (path) {
                var glob = this._findMatchingGlob(data.path);
                if (glob) {
                    var pathKeys = Object.keys(data.path[glob]);
                    return _.union(currentKeyList, pathKeys);
                }
            }
            return currentKeyList;
        }
    };

    function PreferencesManager() {
        this._knownPrefs = {};
        this._scopes = {
            "default": new Scope(new MemoryStorage())
        };

        // Memory-based scope loads synchronously
        this._scopes["default"].load();
        this._scopeOrder = ["default"];
        this._pendingScopes = {};

        this._layers = {};
        this._layerGetters = [];
        this._layerKeys = [];

        this._saveInProgress = false;
        this._nextSaveDeferred = null;
    }

    PreferencesManager.prototype = {
        definePreference: function (id, type, initial, options) {
            options = options || {};
            if (this._knownPrefs.hasOwnProperty(id)) {
                throw new Error("Preference " + id + " was redefined");
            }
            this._knownPrefs[id] = {
                type: type,
                initial: initial,
                name: options.name,
                description: options.description
            };
            this.setValue("default", id, initial);
        },

        addToScopeOrder: function (id, addBefore) {
            if (!addBefore) {
                this._scopeOrder.unshift(id);
            } else {
                var addIndex = this._scopeOrder.indexOf(addBefore);
                if (addIndex > -1) {
                    this._scopeOrder.splice(addIndex, 0, id);
                } else {
                    var queue = this._pendingScopes[addBefore];
                    if (!queue) {
                        queue = [];
                        this._pendingScopes[addBefore] = queue;
                    }
                    queue.unshift(id);
                }
            }
            if (this._pendingScopes[id]) {
                var pending = this._pendingScopes[id];
                delete this._pendingScopes[id];
                pending.forEach(function (scopeID) {
                    this.addToScopeOrder(scopeID, id);
                }.bind(this));
            }
        },

        addScope: function (id, scope, addBefore) {
            if (this._scopes[id]) {
                throw new Error("Attempt to redefine preferences scope: " + id);
            }

            // Check to see if "scope" might be a Storage instead
            if (!scope.getValue) {
                scope = new Scope(scope);
            }

            var deferred = $.Deferred();

            scope.load()
                .then(function () {
                    this._inspectChangesAndFire(function () {
                        this._scopes[id] = scope;
                        this.addToScopeOrder(id, addBefore);
                    }.bind(this));
                    deferred.resolve(id, scope);
                }.bind(this))
                .fail(function (err) {
                    // With preferences, it is valid for there to be no file.
                    // It is not valid to have an unparseable file.
                    if (err instanceof ParsingError) {
                        console.error(err);
                    }
                });

            return deferred.promise();
        },

        removeScope: function (id) {
            this._inspectChangesAndFire(function () {
                delete this._scopes[id];
                var scopeIndex = this._scopeOrder.indexOf(id);
                if (scopeIndex > -1) {
                    this._scopeOrder.splice(scopeIndex, 1);
                }
            }.bind(this));
        },

        _getAllKeysAndPrefs: function (extraKeys) {
            var layerKeys = this._layerKeys;

            var keys = _.map(this._scopes, function (scope) {
                return scope.getKeys(layerKeys);
            });

            keys = _.union.apply(null, keys);
            if (extraKeys) {
                keys = _.union(keys, extraKeys);
            }

            var prefs = _.object(keys, _.map(keys, function (key) {
                return this.getValue(key);
            }.bind(this)));

            return {
                keys: keys,
                prefs: prefs
            };
        },

        _inspectChangesAndFire: function (operation) {
            var before = this._getAllKeysAndPrefs();

            operation();

            var after = this._getAllKeysAndPrefs(before.keys);

            this._fireChanges(before, after);
        },

        _fireChanges: function (before, after) {
            var differentProps = _.map(after.keys, function (key) {
                var beforeValue = before.prefs[key],
                    afterValue = after.prefs[key];

                if (afterValue !== beforeValue) {
                    return {
                        id: key,
                        oldValue: beforeValue,
                        newValue: afterValue
                    };
                }
                return undefined;
            });

            var self = $(this);
            differentProps.forEach(function (data) {
                if (data !== undefined) {
                    self.trigger("preferenceChange", data);
                }
            });

        },

        addLayer: function (id, layer) {
            if (this._layers[id]) {
                throw new Error("Attempt to redefine preferences layer: " + id);
            }

            this._inspectChangesAndFire(function () {
                this._layers[id] = layer;
                this._layerGetters.push(layer.getValue.bind(layer));
                this._layerKeys.push(layer.getKeys.bind(layer));
            }.bind(this));

            $(layer).on("beforeLayerChange", this._beforeLayerChange.bind(this));
            $(layer).on("afterLayerChange", this._afterLayerChange.bind(this));
        },

        _beforeLayerChange: function () {
            this._preLayerChangeData = this._getAllKeysAndPrefs();
        },

        _afterLayerChange: function () {
            if (!this._preLayerChangeData) {
                console.error("After layer change fired without before layer change. This should not happen.");
                return;
            }

            var before = this._preLayerChangeData;
            delete this._preLayerChangeData;

            var after = this._getAllKeysAndPrefs(before.keys);

            this._fireChanges(before, after);
        },

        setValue: function (scopeName, id, value) {
            var scope = this._scopes[scopeName];
            if (!scope) {
                throw new Error("Attempt to set preference in non-existent scope: " + scopeName);
            }

            var oldValue = this.getValue(id);
            scope.setValue(id, value);

            var newValue = this.getValue(id);

            if (oldValue !== newValue) {
                $(this).trigger(PREFERENCE_CHANGE, {
                    id: id,
                    newValue: newValue,
                    oldValue: oldValue
                });
            }
        },

        getValue: function (id) {
            var scopeCounter,
                scopeOrder = this._scopeOrder,
                layerGetters = this._layerGetters;

            for (scopeCounter = 0; scopeCounter < scopeOrder.length; scopeCounter++) {
                var scope = this._scopes[scopeOrder[scopeCounter]];
                var result = scope.getValue(id, layerGetters);
                if (result !== undefined) {
                    return result;
                }
            }
        },

        save: function () {
            if (this._saveInProgress) {
                if (!this._nextSaveDeferred) {
                    this._nextSaveDeferred = $.Deferred();
                }
                return this._nextSaveDeferred.promise();
            }

            this._saveInProgress = true;
            var deferred = this._nextSaveDeferred || $.Deferred();
            this._nextSaveDeferred = null;

            Async.doInParallel(this._scopeOrder, function (id) {
                var scope = this._scopes[id];
                return scope.save();
            }.bind(this))
                .then(function () {
                    this._saveInProgress = false;
                    if (this._nextSaveDeferred) {
                        this.save();
                    }
                    deferred.resolve();
                }.bind(this))
                .fail(function (err) {
                    deferred.reject(err);
                });

            return deferred.promise();
        }
    };

    exports.PreferencesManager = PreferencesManager;
    exports.Scope = Scope;
    exports.MemoryStorage = MemoryStorage;
    exports.LanguageLayer = LanguageLayer;
    exports.PathLayer = PathLayer;
    exports.FileStorage = FileStorage;
});<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */

var config    = require('./config.js');
var emitter   = require('events').EventEmitter;
var log       = require('logule').init(module, 'nfc');
var NFC       = require('nfc').nfc;
var util      = require('util');

/**
 * nfc
 *
 * @constructor
 */

function nfc() {

  /**
   * nfc instance
   * @type {NFC}
   * @private
   */

  this._nfc = new NFC();

  /**
   * NFC reader spews ~100mps, use this for rate limiting
   *
   * @type {boolean}
   */

  this._nfcTagActive = false;

  /**
   * nfc scanning?
   * @type {boolean}
   */

  this._running = false;
}

/**
 * Inherit event.emitter
 */

util.inherits(nfc, emitter);

/**
 * start nfc, set up handlers
 */

nfc.prototype.start = function() {

  log.info('starting nfc');

  var self = this;

  if (!this.running) {
    this.running = true;
    this._nfc.start();

    /**
     * add nfc detection handlers
     * @see https://github.com/camme/node-nfc
     */

    this._nfc.on('uid', function(uid) {
      self._handleNfc(uid, uid.toString('hex'));
    });
  }
};

/**
 * stop nfc, remove handlers
 */

nfc.prototype.stop = function() {

  log.info('stopping nfc');

  if (this.running) {
   this.running = false;
   this._nfc.stop();
   this._nfc.removeAllListeners();
 }
};

/**
 *
 * @param type
 * @param id
 * @private
 */

nfc.prototype._handleNfc = function(type, id) {
  var self = this;

  if (!this.nfcTagActive) {
    this.nfcTagActive = true;
    this.emit(type, id);

    /**
     * Reset nfc tag status
     * Ignore messages for `nfcTimeout` ms
     */

    setTimeout(function resetNfcActive() {
      self.nfcTagActive = false;
    }, config.nfcTimeout);
  }
};

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

module.exports = new nfc();
<commit_msg>fixing active tag<commit_after>
/**
 * Module dependencies
 */

var config    = require('./config.js');
var emitter   = require('events').EventEmitter;
var log       = require('logule').init(module, 'nfc');
var NFC       = require('nfc').nfc;
var util      = require('util');

/**
 * nfc
 *
 * @constructor
 */

function nfc() {

  /**
   * nfc instance
   * @type {NFC}
   * @private
   */

  this._nfc = new NFC();

  /**
   * NFC reader spews ~100mps, use this for rate limiting
   *
   * @type {boolean}
   */

  this._nfcTagActive = false;

  /**
   * nfc scanning?
   * @type {boolean}
   */

  this._running = false;
}

/**
 * Inherit event.emitter
 */

util.inherits(nfc, emitter);

/**
 * start nfc, set up handlers
 */

nfc.prototype.start = function() {

  log.info('starting nfc');

  var self = this;

  if (!this.running) {
    this.running = true;
    this._nfc.start();

    /**
     * add nfc detection handlers
     * @see https://github.com/camme/node-nfc
     */

    this._nfc.on('uid', function(uid) {
      self._handleNfc(uid, uid.toString('hex'));
    });
  }
};

/**
 * stop nfc, remove handlers
 */

nfc.prototype.stop = function() {

  log.info('stopping nfc');

  if (this.running) {
   this.running = false;
   this._nfc.stop();
   this._nfc.removeAllListeners();
 }
};

/**
 *
 * @param type
 * @param id
 * @private
 */

nfc.prototype._handleNfc = function(type, id) {
  var self = this;

  if (!this._nfcTagActive) {
    this._nfcTagActive = true;
    this.emit(type, id);

    /**
     * Reset nfc tag status
     * Ignore messages for `nfcTimeout` ms
     */

    setTimeout(function resetNfcActive() {
      self._nfcTagActive = false;
    }, config.nfcTimeout);
  }
};

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

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


<commit_before>
/**
 * Module dependencies.
 */

var thunkify = require('thunkify-wrap');
var request = require('request');
var cheerio = require('cheerio');
var moment = require('moment');

/**
 * Define static variables.
 */

var get = thunkify(request.get);

/**
 * Get packages.
 *
 * @param {String} username
 *
 * @return {Array}
 */

exports.packages = function *(username) {
  if (username.length === 0) return;
  var domain = 'https://www.npmjs.com/~' + username;
  var $ = yield parseHTML(domain);
  var packages = [];
  for (var i = 0; i < $('.content li > a').length; i++) {
    packages.push($('.content li > a')[i].children[0].data);
  }
  return packages;
};

/**
 * Get package download info.
 *
 * @param {String} pkg
 * @param {String} from
 * @param {String} to
 *
 * @return {Object}
 */

exports.downloads = function *(pkg, from, to) {
  var domain = 'https://api.npmjs.org/downloads/range/';
  var range = 'last-month';
  if (from && to) range = from + ':' + to;
  domain += range + '/' + pkg;
  var data = yield parseJSON(domain);
  var start = moment.isDate(from) ? from : moment().subtract(1, 'month').format('YYYY-MM-DD');
  var end = moment.isDate(to) ? to : moment().subtract(1, 'day').format('YYYY-MM-DD');
  data.downloads = normalize(data.downloads, start, end);
  return data;
};

/**
 * Normalize start date.
 */

function normalize(arr, start, end) {
  if (!moment(arr[arr.length - 1].day).isSame(moment(end))) arr.push({ day: end, downloads: 0 });
  if (moment(arr[0].day).isSame(moment(start))) return fill(arr);
  arr.unshift({ day: start, downloads: 0 });
  return fill(arr);
}

/**
 * Fill in blank dates.
 */

function fill(arr) {
  var prev = curr = '';
  for (var i = 1; i < arr.length; i++) {
    prev = moment(arr[i - 1].day);
    curr = moment(arr[i].day);
    if (prev.add(1, 'd').isSame(curr)) continue;
    var newcurr = prev.format('YYYY-MM-DD');
    arr.splice(i, 0, { day: newcurr, downloads: 0 });
  }
  return arr;
}

/**
 * Get HTML and parse it.
 *
 * @param {String} domain
 *
 * @return {Object}
 */

function *parseHTML(domain) {
  var html = yield get(domain);
  return cheerio.load(html[0].body);
}

/**
 * Get JSON and parse it.
 *
 * @param {String} domain
 *
 * @return {Object}
 */

function *parseJSON(domain) {
  var res = yield get(domain);
  return JSON.parse(res[0].body);
}
<commit_msg>weird edge cases<commit_after>
/**
 * Module dependencies.
 */

var thunkify = require('thunkify-wrap');
var request = require('request');
var cheerio = require('cheerio');
var moment = require('moment');

/**
 * Define static variables.
 */

var get = thunkify(request.get);

/**
 * Get packages.
 *
 * @param {String} username
 *
 * @return {Array}
 */

exports.packages = function *(username) {
  if (username.length === 0) return;
  var domain = 'https://www.npmjs.com/~' + username;
  var $ = yield parseHTML(domain);
  var packages = [];
  for (var i = 0; i < $('.content li > a').length; i++) {
    packages.push($('.content li > a')[i].children[0].data);
  }
  return packages;
};

/**
 * Get package download info.
 *
 * @param {String} pkg
 * @param {String} from
 * @param {String} to
 *
 * @return {Object}
 */

exports.downloads = function *(pkg, from, to) {
  var domain = 'https://api.npmjs.org/downloads/range/';
  var range = 'last-month';
  if (from && to) range = from + ':' + to;
  domain += range + '/' + pkg;
  var data = yield parseJSON(domain);
  var start = moment.isDate(from) ? from : moment().subtract(1, 'month').subtract(1, 'day').format('YYYY-MM-DD');
  var end = moment.isDate(to) ? to : moment().format('YYYY-MM-DD');
  data.downloads = trim(data.downloads, start, end);
  return data;
};

/**
 * Trim start date.
 */

function trim(arr, start, end) {
  if (!moment(arr[arr.length - 1].day).isSame(moment(end))) arr.push({ day: end, downloads: 0 });
  if (moment(arr[0].day).isSame(moment(start))) return fill(arr);
  arr.unshift({ day: start, downloads: 0 });
  return fill(arr);
}

/**
 * Fill in blank dates.
 */

function fill(arr) {
  var prev = curr = '';
  console.log(arr);
  for (var i = 1; i < arr.length; i++) {
    prev = moment(arr[i - 1].day);
    curr = moment(arr[i].day);
    if (prev.add(1, 'd').isSame(curr)) continue;
    var newcurr = prev.format('YYYY-MM-DD');
    arr.splice(i, 0, { day: newcurr, downloads: 0 });
  }
  return arr;
}

/**
 * Get HTML and parse it.
 *
 * @param {String} domain
 *
 * @return {Object}
 */

function *parseHTML(domain) {
  var html = yield get(domain);
  return cheerio.load(html[0].body);
}

/**
 * Get JSON and parse it.
 *
 * @param {String} domain
 *
 * @return {Object}
 */

function *parseJSON(domain) {
  var res = yield get(domain);
  return JSON.parse(res[0].body);
}
<|endoftext|>


<commit_before>;(function () {
  // windows: running 'npm blah' in this folder will invoke WSH, not node.
  /*globals WScript*/
  if (typeof WScript !== 'undefined') {
    WScript.echo(
      'npm does not work when run\n' +
      'with the Windows Scripting Host\n\n' +
      '"cd" to a different directory,\n' +
      'or type "npm.cmd <args>",\n' +
      'or type "node npm <args>".'
    )
    WScript.quit(1)
    return
  }

  var EventEmitter = require('events').EventEmitter
  var npm = module.exports = new EventEmitter()
  var npmconf = require('./config/core.js')
  var log = require('npmlog')
  var fs = require('graceful-fs')
  var path = require('path')
  var abbrev = require('abbrev')
  var which = require('which')
  var CachingRegClient = require('./cache/caching-client.js')

  npm.config = {
    loaded: false,
    get: function () {
      throw new Error('npm.load() required')
    },
    set: function () {
      throw new Error('npm.load() required')
    }
  }

  npm.commands = {}

  npm.rollbacks = []

  try {
    // startup, ok to do this synchronously
    var j = JSON.parse(fs.readFileSync(
      path.join(__dirname, '../package.json')) + '')
    npm.version = j.version
  } catch (ex) {
    try {
      log.info('error reading version', ex)
    } catch (er) {}
    npm.version = ex
  }

  var commandCache = {}

  // short names for common things
  var aliases = {
    'rm': 'uninstall',
    'r': 'uninstall',
    'un': 'uninstall',
    'unlink': 'uninstall',
    'remove': 'uninstall',
    'rb': 'rebuild',
    'list': 'ls',
    'la': 'ls',
    'll': 'ls',
    'ln': 'link',
    'i': 'install',
    'isntall': 'install',
    'up': 'update',
    'upgrade': 'update',
    'c': 'config',
    'dist-tags': 'dist-tag',
    'info': 'view',
    'show': 'view',
    'find': 'search',
    's': 'search',
    'se': 'search',
    'author': 'owner',
    'home': 'docs',
    'issues': 'bugs',
    'unstar': 'star', // same function
    'apihelp': 'help',
    'login': 'adduser',
    'add-user': 'adduser',
    'tst': 'test',
    't': 'test',
    'find-dupes': 'dedupe',
    'ddp': 'dedupe',
    'v': 'view',
    'verison': 'version'
  }

  var aliasNames = Object.keys(aliases)

  // these are filenames in .
  var cmdList = [
    'install',
    'uninstall',
    'cache',
    'config',
    'set',
    'get',
    'update',
    'outdated',
    'prune',
    'pack',
    'dedupe',

    'rebuild',
    'link',

    'publish',
    'star',
    'stars',
    'tag',
    'adduser',
    'logout',
    'unpublish',
    'owner',
    'access',
    'deprecate',
    'shrinkwrap',

    'help',
    'help-search',
    'ls',
    'search',
    'view',
    'init',
    'version',
    'edit',
    'explore',
    'docs',
    'repo',
    'bugs',
    'faq',
    'root',
    'prefix',
    'bin',
    'whoami',
    'dist-tag',
    'ping',

    'test',
    'stop',
    'start',
    'restart',
    'run-script',
    'completion'
  ]
  var plumbing = [
    'build',
    'unbuild',
    'xmas',
    'substack',
    'visnup'
  ]
  var littleGuys = [ 'isntall' ]
  var fullList = cmdList.concat(aliasNames).filter(function (c) {
        return plumbing.indexOf(c) === -1
      })
  var abbrevs = abbrev(fullList)

  // we have our reasons
  fullList = npm.fullList = fullList.filter(function (c) {
    return littleGuys.indexOf(c) === -1
  })

  Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
    Object.defineProperty(npm.commands, c, { get: function () {
      if (!loaded) {
        throw new Error(
          'Call npm.load(config, cb) before using this command.\n' +
            'See the README.md or cli.js for example usage.'
        )
      }
      var a = npm.deref(c)
      if (c === 'la' || c === 'll') {
        npm.config.set('long', true)
      }

      npm.command = c
      if (commandCache[a]) return commandCache[a]

      var cmd = require(__dirname + '/' + a + '.js')

      commandCache[a] = function () {
        var args = Array.prototype.slice.call(arguments, 0)
        if (typeof args[args.length - 1] !== 'function') {
          args.push(defaultCb)
        }
        if (args.length === 1) args.unshift([])

        npm.registry.version = npm.version
        if (!npm.registry.refer) {
          npm.registry.refer = [a].concat(args[0]).map(function (arg) {
            // exclude anything that might be a URL, path, or private module
            // Those things will always have a slash in them somewhere
            if (arg && arg.match && arg.match(/\/|\\/)) {
              return '[REDACTED]'
            } else {
              return arg
            }
          }).filter(function (arg) {
            return arg && arg.match
          }).join(' ')
        }

        cmd.apply(npm, args)
      }

      Object.keys(cmd).forEach(function (k) {
        commandCache[a][k] = cmd[k]
      })

      return commandCache[a]
    }, enumerable: fullList.indexOf(c) !== -1, configurable: true })

    // make css-case commands callable via camelCase as well
    if (c.match(/\-([a-z])/)) {
      addCommand(c.replace(/\-([a-z])/g, function (a, b) {
        return b.toUpperCase()
      }))
    }
  })

  function defaultCb (er, data) {
    log.disableProgress()
    if (er) console.error(er.stack || er.message)
    else console.log(data)
  }

  npm.deref = function (c) {
    if (!c) return ''
    if (c.match(/[A-Z]/)) {
      c = c.replace(/([A-Z])/g, function (m) {
        return '-' + m.toLowerCase()
      })
    }
    if (plumbing.indexOf(c) !== -1) return c
    var a = abbrevs[c]
    if (aliases[a]) a = aliases[a]
    return a
  }

  var loaded = false
  var loading = false
  var loadErr = null
  var loadListeners = []

  function loadCb (er) {
    loadListeners.forEach(function (cb) {
      process.nextTick(cb.bind(npm, er, npm))
    })
    loadListeners.length = 0
  }

  npm.load = function (cli, cb_) {
    if (!cb_ && typeof cli === 'function') {
      cb_ = cli
      cli = {}
    }
    if (!cb_) cb_ = function () {}
    if (!cli) cli = {}
    loadListeners.push(cb_)
    if (loaded || loadErr) return cb(loadErr)
    if (loading) return
    loading = true
    var onload = true

    function cb (er) {
      if (loadErr) return
      loadErr = er
      if (er) return cb_(er)
      if (npm.config.get('force')) {
        log.warn('using --force', 'I sure hope you know what you are doing.')
      }
      npm.config.loaded = true
      loaded = true
      loadCb(loadErr = er)
      onload = onload && npm.config.get('onload-script')
      if (onload) {
        require(onload)
        onload = false
      }
    }

    log.pause()

    load(npm, cli, cb)
  }

  function load (npm, cli, cb) {
    which(process.argv[0], function (er, node) {
      if (!er && node.toUpperCase() !== process.execPath.toUpperCase()) {
        log.verbose('node symlink', node)
        process.execPath = node
        process.installPrefix = path.resolve(node, '..', '..')
      }

      // look up configs
      var builtin = path.resolve(__dirname, '..', 'npmrc')
      npmconf.load(cli, builtin, function (er, config) {
        if (er === config) er = null

        npm.config = config
        if (er) return cb(er)

        // if the 'project' config is not a filename, and we're
        // not in global mode, then that means that it collided
        // with either the default or effective userland config
        if (!config.get('global') &&
            config.sources.project &&
            config.sources.project.type !== 'ini') {
          log.verbose(
            'config',
            'Skipping project config: %s. (matches userconfig)',
            config.localPrefix + '/.npmrc'
          )
        }

        // Include npm-version and node-version in user-agent
        var ua = config.get('user-agent') || ''
        ua = ua.replace(/\{node-version\}/gi, process.version)
        ua = ua.replace(/\{npm-version\}/gi, npm.version)
        ua = ua.replace(/\{platform\}/gi, process.platform)
        ua = ua.replace(/\{arch\}/gi, process.arch)
        config.set('user-agent', ua)

        var color = config.get('color')

        log.level = config.get('loglevel')
        log.heading = config.get('heading') || 'npm'
        log.stream = config.get('logstream')

        switch (color) {
          case 'always':
            log.enableColor()
            npm.color = true
            break
          case false:
            log.disableColor()
            npm.color = false
            break
          default:
            var tty = require('tty')
            if (process.stdout.isTTY) npm.color = true
            else if (!tty.isatty) npm.color = true
            else if (tty.isatty(1)) npm.color = true
            else npm.color = false
            break
        }

        log.resume()

        // at this point the configs are all set.
        // go ahead and spin up the registry client.
        npm.registry = new CachingRegClient(npm.config)

        var umask = npm.config.get('umask')
        npm.modes = {
          exec: parseInt('0777', 8) & (~umask),
          file: parseInt('0666', 8) & (~umask),
          umask: umask
        }

        var gp = Object.getOwnPropertyDescriptor(config, 'globalPrefix')
        Object.defineProperty(npm, 'globalPrefix', gp)

        var lp = Object.getOwnPropertyDescriptor(config, 'localPrefix')
        Object.defineProperty(npm, 'localPrefix', lp)

        return cb(null, npm)
      })
    })
  }

  Object.defineProperty(npm, 'prefix',
    { get: function () {
        return npm.config.get('global') ? npm.globalPrefix : npm.localPrefix
      },
      set: function (r) {
        var k = npm.config.get('global') ? 'globalPrefix' : 'localPrefix'
        npm[k] = r
        return r
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'bin',
    { get: function () {
        if (npm.config.get('global')) return npm.globalBin
        return path.resolve(npm.root, '.bin')
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'globalBin',
    { get: function () {
        var b = npm.globalPrefix
        if (process.platform !== 'win32') b = path.resolve(b, 'bin')
        return b
      }
    })

  Object.defineProperty(npm, 'dir',
    { get: function () {
        if (npm.config.get('global')) return npm.globalDir
        return path.resolve(npm.prefix, 'node_modules')
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'globalDir',
    { get: function () {
        return (process.platform !== 'win32')
             ? path.resolve(npm.globalPrefix, 'lib', 'node_modules')
             : path.resolve(npm.globalPrefix, 'node_modules')
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'root',
    { get: function () { return npm.dir } })

  Object.defineProperty(npm, 'cache',
    { get: function () { return npm.config.get('cache') },
      set: function (r) { return npm.config.set('cache', r) },
      enumerable: true
    })

  var tmpFolder
  var rand = require('crypto').randomBytes(4).toString('hex')
  Object.defineProperty(npm, 'tmp',
    { get: function () {
        if (!tmpFolder) tmpFolder = 'npm-' + process.pid + '-' + rand
        return path.resolve(npm.config.get('tmp'), tmpFolder)
      },
      enumerable: true
    })

  // the better to repl you with
  Object.getOwnPropertyNames(npm.commands).forEach(function (n) {
    if (npm.hasOwnProperty(n) || n === 'config') return

    Object.defineProperty(npm, n, { get: function () {
      return function () {
        var args = Array.prototype.slice.call(arguments, 0)
        var cb = defaultCb

        if (args.length === 1 && Array.isArray(args[0])) {
          args = args[0]
        }

        if (typeof args[args.length - 1] === 'function') {
          cb = args.pop()
        }
        npm.commands[n](args, cb)
      }
    }, enumerable: false, configurable: true })
  })

  if (require.main === module) {
    require('../bin/npm-cli.js')
  }
})()
<commit_msg>npm: Globally monkey-patch graceful-fs<commit_after>;(function () {
  // windows: running 'npm blah' in this folder will invoke WSH, not node.
  /*globals WScript*/
  if (typeof WScript !== 'undefined') {
    WScript.echo(
      'npm does not work when run\n' +
      'with the Windows Scripting Host\n\n' +
      '"cd" to a different directory,\n' +
      'or type "npm.cmd <args>",\n' +
      'or type "node npm <args>".'
    )
    WScript.quit(1)
    return
  }

  var gfs = require('graceful-fs')
  // Patch the global fs module here at the app level
  var fs = gfs.gracefulify(require('fs'))

  var EventEmitter = require('events').EventEmitter
  var npm = module.exports = new EventEmitter()
  var npmconf = require('./config/core.js')
  var log = require('npmlog')

  var path = require('path')
  var abbrev = require('abbrev')
  var which = require('which')
  var CachingRegClient = require('./cache/caching-client.js')

  npm.config = {
    loaded: false,
    get: function () {
      throw new Error('npm.load() required')
    },
    set: function () {
      throw new Error('npm.load() required')
    }
  }

  npm.commands = {}

  npm.rollbacks = []

  try {
    // startup, ok to do this synchronously
    var j = JSON.parse(fs.readFileSync(
      path.join(__dirname, '../package.json')) + '')
    npm.version = j.version
  } catch (ex) {
    try {
      log.info('error reading version', ex)
    } catch (er) {}
    npm.version = ex
  }

  var commandCache = {}

  // short names for common things
  var aliases = {
    'rm': 'uninstall',
    'r': 'uninstall',
    'un': 'uninstall',
    'unlink': 'uninstall',
    'remove': 'uninstall',
    'rb': 'rebuild',
    'list': 'ls',
    'la': 'ls',
    'll': 'ls',
    'ln': 'link',
    'i': 'install',
    'isntall': 'install',
    'up': 'update',
    'upgrade': 'update',
    'c': 'config',
    'dist-tags': 'dist-tag',
    'info': 'view',
    'show': 'view',
    'find': 'search',
    's': 'search',
    'se': 'search',
    'author': 'owner',
    'home': 'docs',
    'issues': 'bugs',
    'unstar': 'star', // same function
    'apihelp': 'help',
    'login': 'adduser',
    'add-user': 'adduser',
    'tst': 'test',
    't': 'test',
    'find-dupes': 'dedupe',
    'ddp': 'dedupe',
    'v': 'view',
    'verison': 'version'
  }

  var aliasNames = Object.keys(aliases)

  // these are filenames in .
  var cmdList = [
    'install',
    'uninstall',
    'cache',
    'config',
    'set',
    'get',
    'update',
    'outdated',
    'prune',
    'pack',
    'dedupe',

    'rebuild',
    'link',

    'publish',
    'star',
    'stars',
    'tag',
    'adduser',
    'logout',
    'unpublish',
    'owner',
    'access',
    'deprecate',
    'shrinkwrap',

    'help',
    'help-search',
    'ls',
    'search',
    'view',
    'init',
    'version',
    'edit',
    'explore',
    'docs',
    'repo',
    'bugs',
    'faq',
    'root',
    'prefix',
    'bin',
    'whoami',
    'dist-tag',
    'ping',

    'test',
    'stop',
    'start',
    'restart',
    'run-script',
    'completion'
  ]
  var plumbing = [
    'build',
    'unbuild',
    'xmas',
    'substack',
    'visnup'
  ]
  var littleGuys = [ 'isntall' ]
  var fullList = cmdList.concat(aliasNames).filter(function (c) {
        return plumbing.indexOf(c) === -1
      })
  var abbrevs = abbrev(fullList)

  // we have our reasons
  fullList = npm.fullList = fullList.filter(function (c) {
    return littleGuys.indexOf(c) === -1
  })

  Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
    Object.defineProperty(npm.commands, c, { get: function () {
      if (!loaded) {
        throw new Error(
          'Call npm.load(config, cb) before using this command.\n' +
            'See the README.md or cli.js for example usage.'
        )
      }
      var a = npm.deref(c)
      if (c === 'la' || c === 'll') {
        npm.config.set('long', true)
      }

      npm.command = c
      if (commandCache[a]) return commandCache[a]

      var cmd = require(__dirname + '/' + a + '.js')

      commandCache[a] = function () {
        var args = Array.prototype.slice.call(arguments, 0)
        if (typeof args[args.length - 1] !== 'function') {
          args.push(defaultCb)
        }
        if (args.length === 1) args.unshift([])

        npm.registry.version = npm.version
        if (!npm.registry.refer) {
          npm.registry.refer = [a].concat(args[0]).map(function (arg) {
            // exclude anything that might be a URL, path, or private module
            // Those things will always have a slash in them somewhere
            if (arg && arg.match && arg.match(/\/|\\/)) {
              return '[REDACTED]'
            } else {
              return arg
            }
          }).filter(function (arg) {
            return arg && arg.match
          }).join(' ')
        }

        cmd.apply(npm, args)
      }

      Object.keys(cmd).forEach(function (k) {
        commandCache[a][k] = cmd[k]
      })

      return commandCache[a]
    }, enumerable: fullList.indexOf(c) !== -1, configurable: true })

    // make css-case commands callable via camelCase as well
    if (c.match(/\-([a-z])/)) {
      addCommand(c.replace(/\-([a-z])/g, function (a, b) {
        return b.toUpperCase()
      }))
    }
  })

  function defaultCb (er, data) {
    log.disableProgress()
    if (er) console.error(er.stack || er.message)
    else console.log(data)
  }

  npm.deref = function (c) {
    if (!c) return ''
    if (c.match(/[A-Z]/)) {
      c = c.replace(/([A-Z])/g, function (m) {
        return '-' + m.toLowerCase()
      })
    }
    if (plumbing.indexOf(c) !== -1) return c
    var a = abbrevs[c]
    if (aliases[a]) a = aliases[a]
    return a
  }

  var loaded = false
  var loading = false
  var loadErr = null
  var loadListeners = []

  function loadCb (er) {
    loadListeners.forEach(function (cb) {
      process.nextTick(cb.bind(npm, er, npm))
    })
    loadListeners.length = 0
  }

  npm.load = function (cli, cb_) {
    if (!cb_ && typeof cli === 'function') {
      cb_ = cli
      cli = {}
    }
    if (!cb_) cb_ = function () {}
    if (!cli) cli = {}
    loadListeners.push(cb_)
    if (loaded || loadErr) return cb(loadErr)
    if (loading) return
    loading = true
    var onload = true

    function cb (er) {
      if (loadErr) return
      loadErr = er
      if (er) return cb_(er)
      if (npm.config.get('force')) {
        log.warn('using --force', 'I sure hope you know what you are doing.')
      }
      npm.config.loaded = true
      loaded = true
      loadCb(loadErr = er)
      onload = onload && npm.config.get('onload-script')
      if (onload) {
        require(onload)
        onload = false
      }
    }

    log.pause()

    load(npm, cli, cb)
  }

  function load (npm, cli, cb) {
    which(process.argv[0], function (er, node) {
      if (!er && node.toUpperCase() !== process.execPath.toUpperCase()) {
        log.verbose('node symlink', node)
        process.execPath = node
        process.installPrefix = path.resolve(node, '..', '..')
      }

      // look up configs
      var builtin = path.resolve(__dirname, '..', 'npmrc')
      npmconf.load(cli, builtin, function (er, config) {
        if (er === config) er = null

        npm.config = config
        if (er) return cb(er)

        // if the 'project' config is not a filename, and we're
        // not in global mode, then that means that it collided
        // with either the default or effective userland config
        if (!config.get('global') &&
            config.sources.project &&
            config.sources.project.type !== 'ini') {
          log.verbose(
            'config',
            'Skipping project config: %s. (matches userconfig)',
            config.localPrefix + '/.npmrc'
          )
        }

        // Include npm-version and node-version in user-agent
        var ua = config.get('user-agent') || ''
        ua = ua.replace(/\{node-version\}/gi, process.version)
        ua = ua.replace(/\{npm-version\}/gi, npm.version)
        ua = ua.replace(/\{platform\}/gi, process.platform)
        ua = ua.replace(/\{arch\}/gi, process.arch)
        config.set('user-agent', ua)

        var color = config.get('color')

        log.level = config.get('loglevel')
        log.heading = config.get('heading') || 'npm'
        log.stream = config.get('logstream')

        switch (color) {
          case 'always':
            log.enableColor()
            npm.color = true
            break
          case false:
            log.disableColor()
            npm.color = false
            break
          default:
            var tty = require('tty')
            if (process.stdout.isTTY) npm.color = true
            else if (!tty.isatty) npm.color = true
            else if (tty.isatty(1)) npm.color = true
            else npm.color = false
            break
        }

        log.resume()

        // at this point the configs are all set.
        // go ahead and spin up the registry client.
        npm.registry = new CachingRegClient(npm.config)

        var umask = npm.config.get('umask')
        npm.modes = {
          exec: parseInt('0777', 8) & (~umask),
          file: parseInt('0666', 8) & (~umask),
          umask: umask
        }

        var gp = Object.getOwnPropertyDescriptor(config, 'globalPrefix')
        Object.defineProperty(npm, 'globalPrefix', gp)

        var lp = Object.getOwnPropertyDescriptor(config, 'localPrefix')
        Object.defineProperty(npm, 'localPrefix', lp)

        return cb(null, npm)
      })
    })
  }

  Object.defineProperty(npm, 'prefix',
    { get: function () {
        return npm.config.get('global') ? npm.globalPrefix : npm.localPrefix
      },
      set: function (r) {
        var k = npm.config.get('global') ? 'globalPrefix' : 'localPrefix'
        npm[k] = r
        return r
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'bin',
    { get: function () {
        if (npm.config.get('global')) return npm.globalBin
        return path.resolve(npm.root, '.bin')
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'globalBin',
    { get: function () {
        var b = npm.globalPrefix
        if (process.platform !== 'win32') b = path.resolve(b, 'bin')
        return b
      }
    })

  Object.defineProperty(npm, 'dir',
    { get: function () {
        if (npm.config.get('global')) return npm.globalDir
        return path.resolve(npm.prefix, 'node_modules')
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'globalDir',
    { get: function () {
        return (process.platform !== 'win32')
             ? path.resolve(npm.globalPrefix, 'lib', 'node_modules')
             : path.resolve(npm.globalPrefix, 'node_modules')
      },
      enumerable: true
    })

  Object.defineProperty(npm, 'root',
    { get: function () { return npm.dir } })

  Object.defineProperty(npm, 'cache',
    { get: function () { return npm.config.get('cache') },
      set: function (r) { return npm.config.set('cache', r) },
      enumerable: true
    })

  var tmpFolder
  var rand = require('crypto').randomBytes(4).toString('hex')
  Object.defineProperty(npm, 'tmp',
    { get: function () {
        if (!tmpFolder) tmpFolder = 'npm-' + process.pid + '-' + rand
        return path.resolve(npm.config.get('tmp'), tmpFolder)
      },
      enumerable: true
    })

  // the better to repl you with
  Object.getOwnPropertyNames(npm.commands).forEach(function (n) {
    if (npm.hasOwnProperty(n) || n === 'config') return

    Object.defineProperty(npm, n, { get: function () {
      return function () {
        var args = Array.prototype.slice.call(arguments, 0)
        var cb = defaultCb

        if (args.length === 1 && Array.isArray(args[0])) {
          args = args[0]
        }

        if (typeof args[args.length - 1] === 'function') {
          cb = args.pop()
        }
        npm.commands[n](args, cb)
      }
    }, enumerable: false, configurable: true })
  })

  if (require.main === module) {
    require('../bin/npm-cli.js')
  }
})()
<|endoftext|>