<commit_before>(function(_, $, Backbone) {

  var root = this;
  var Kinetic = root.Kinetic = {};

  // Template hash.
  Kinetic.templates = {};

  // Detect script tags in an html string.
  var rscript  = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;

  var View = Kinetic.View = Backbone.View.extend({

    constructor: function() {
      this.views = [];
      View.__super__.constructor.apply(this, arguments);
    },

    render: function() {
      var i, el, selector, elements, attr, view, data;
      var attrs = Kinetic.attrs;

      // Destroy old views.
      while (view = this.views.pop()) {
        view.destroy();
      }

      // Render the template if it exists.
      this.$el.html(this.template ? this.template() : '');

      // Create the selector if it hasn't been created already.
      if (!Kinetic.selector) {
        selector = [];
        for (attr in attrs) selector.push('[data-' + dash(attr) + ']');
        Kinetic.selector = selector.join(',');
      }

      // Find all elements with appropriate attributes.
      elements = this.$(Kinetic.selector).get();

      // Create a view for each element/attr pair.
      while (el = elements.pop()) {
        data = $(el).data();
        for (attr in data) {
          if (!attrs[attr]) continue;
          view = attrs[attr].call(this, el, data[attr]);
          if (view) this.views.push(view.render());
        }
      }

      return this;
    },

    destroy: function() {
      _.invoke(this.views, 'destroy');
      if (this.model) this.model.off(null, null, this);
      if (this.collection) this.collection.off(null, null, this);
    }

  });

  var Content = View.extend({

    // Render initial content and re-render on changes to the model.
    initialize: function() {
      _.defaults(this.options, {noScript: true});
      this.model.on('change', this.change, this);
    },

    change: function() {
      if (!this.model.hasChanged(this.options.attr)) return;
      this.render();
    },

    render: function() {
      var value = this.model.get(this.options.attr);
      if (this.options.noScript) value = (value + '').replace(rscript, '');
      this.$el[this.options.accessor](value);
      return this;
    }

  });

  var Template = Kinetic.View.extend({

    template: function() {
      var layout = this.options.layout;
      var templates = Kinetic.templates;
      var template = templates[this.options.name];
      var data = {
        view: this,
        model: this.model,
        collection: this.collection
      };

      // If no layout is specified, just render the template.
      if (!layout) return template(data);

      // Set hooks for the layout to render the template.
      data.data = data;
      data.template = template;
      return templates[layout](data);
    }

  });

  Kinetic.attrs = {

    ref: function(el, options) {
      this['$' + options] = $(el);
    },

    template: function(el, options) {

      // If `options` is a string, assume it's the template name.
      if (_.isString(options)) options = {name: options};

      // Set model/collection from provided properties.
      if (options.model) {
        options.model = this.model[options.model];
      }
      if (options.collection) {
        options.collection = this.model[options.collection];
      }

      _.defaults(options, {
        model: this.model,
        collection: this.collection
      });

      options.el = el;
      return new Template(options);
    }

  };

  // Html and text content views.
  _.each(['html', 'text'], function(accessor) {

    Kinetic.attrs[accessor] = function(el, options) {

      // If `options` is a string, assume it's an attribute.
      if (_.isString(options)) options = {attr: options};

      return new Content(_.extend(options, {
        el: el,
        model: this.model,
        accessor: accessor
      }));
    };

  });

  // Replace upper-case characters with a dash and their lower case version.
  var dash = function(s) {
    return s.replace(/[A-Z]/g, function(c){ return '-' + c.toLowerCase(); });
  };

}).call(this, _, jQuery, Backbone);
<commit_msg>Use dash technique from jQuery.<commit_after>(function(_, $, Backbone) {

  var root = this;
  var Kinetic = root.Kinetic = {};

  // Template hash.
  Kinetic.templates = {};

  // Detect script tags in an html string.
  var rscript  = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;

  var View = Kinetic.View = Backbone.View.extend({

    constructor: function() {
      this.views = [];
      View.__super__.constructor.apply(this, arguments);
    },

    render: function() {
      var i, el, selector, elements, attr, view, data;
      var attrs = Kinetic.attrs;

      // Destroy old views.
      while (view = this.views.pop()) {
        view.destroy();
      }

      // Render the template if it exists.
      this.$el.html(this.template ? this.template() : '');

      // Create the selector if it hasn't been created already.
      if (!Kinetic.selector) {
        selector = [];
        for (attr in attrs) selector.push('[data-' + dash(attr) + ']');
        Kinetic.selector = selector.join(',');
      }

      // Find all elements with appropriate attributes.
      elements = this.$(Kinetic.selector).get();

      // Create a view for each element/attr pair.
      while (el = elements.pop()) {
        data = $(el).data();
        for (attr in data) {
          if (!attrs[attr]) continue;
          view = attrs[attr].call(this, el, data[attr]);
          if (view) this.views.push(view.render());
        }
      }

      return this;
    },

    destroy: function() {
      _.invoke(this.views, 'destroy');
      if (this.model) this.model.off(null, null, this);
      if (this.collection) this.collection.off(null, null, this);
    }

  });

  var Content = View.extend({

    // Render initial content and re-render on changes to the model.
    initialize: function() {
      _.defaults(this.options, {noScript: true});
      this.model.on('change', this.change, this);
    },

    change: function() {
      if (!this.model.hasChanged(this.options.attr)) return;
      this.render();
    },

    render: function() {
      var value = this.model.get(this.options.attr);
      if (this.options.noScript) value = (value + '').replace(rscript, '');
      this.$el[this.options.accessor](value);
      return this;
    }

  });

  var Template = Kinetic.View.extend({

    template: function() {
      var layout = this.options.layout;
      var templates = Kinetic.templates;
      var template = templates[this.options.name];
      var data = {
        view: this,
        model: this.model,
        collection: this.collection
      };

      // If no layout is specified, just render the template.
      if (!layout) return template(data);

      // Set hooks for the layout to render the template.
      data.data = data;
      data.template = template;
      return templates[layout](data);
    }

  });

  Kinetic.attrs = {

    ref: function(el, options) {
      this['$' + options] = $(el);
    },

    template: function(el, options) {

      // If `options` is a string, assume it's the template name.
      if (_.isString(options)) options = {name: options};

      // Set model/collection from provided properties.
      if (options.model) {
        options.model = this.model[options.model];
      }
      if (options.collection) {
        options.collection = this.model[options.collection];
      }

      _.defaults(options, {
        model: this.model,
        collection: this.collection
      });

      options.el = el;
      return new Template(options);
    }

  };

  // Html and text content views.
  _.each(['html', 'text'], function(accessor) {

    Kinetic.attrs[accessor] = function(el, options) {

      // If `options` is a string, assume it's an attribute.
      if (_.isString(options)) options = {attr: options};

      return new Content(_.extend(options, {
        el: el,
        model: this.model,
        accessor: accessor
      }));
    };

  });

  // Replace upper-case characters with a dash and their lower case version.
  var dash = function(s) {
    return s.replace(/([A-Z])/g, '-$1').toLowerCase();
  };

}).call(this, _, jQuery, Backbone);
<|endoftext|>


<commit_before>// moment.js language configuration
// language : breton (br)
// author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou

function relativeTimeWithMutation(number, withoutSuffix, key) {
    var format = {
        'mm': "munutenn",
        'MM': "miz",
        'dd': "devezh"
    };
    return number + ' ' + mutation(format[key], number);
}

function specialMutationForYears(number) {
    switch (lastNumber(number)) {
        case 1:
        case 3:
        case 4:
        case 5:
        case 9:
            return number + ' bloaz';
        default:
            return number + ' vloaz';

    }
}

function lastNumber(number) {
    if (number > 9) {
        return lastNumber(number % 10);
    }
    return number;
}

function mutation(text, number) {
    if (number === 2) {
        return softMutation(text);
    }
    return text;
}

function softMutation(text) {
    var mutationTable = {
        'm': 'v',
        'b': 'v',
        'd': 'z'
    };
    if (mutationTable[text.charAt(0)] === undefined) {
        return text;
    }
    return mutationTable[text.charAt(0)] + text.substring(1);
}

require('../moment').lang('br', {
    months : "Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),
    monthsShort : "Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),
    weekdays : "Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"),
    weekdaysShort : "Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),
    weekdaysMin : "Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),
    longDateFormat : {
        LT : "h[e]mm A",
        L : "DD/MM/YYYY",
        LL : "D [a viz] MMMM YYYY",
        LLL : "D [a viz] MMMM YYYY LT",
        LLLL : "dddd, D [a viz] MMMM YYYY LT"
    },
    calendar : {
        sameDay : '[Hiziv da] LT',
        nextDay : '[Warc\'hoazh da] LT',
        nextWeek : 'dddd [da] LT',
        lastDay : '[Dec\'h da] LT',
        lastWeek : 'dddd [paset da] LT',
        sameElse : 'L'
    },
    relativeTime : {
        future : "a-benn %s",
        past : "%s 'zo",
        s : "un nebeud segondennoù",
        m : "ur vunutenn",
        mm : relativeTimeWithMutation,
        h : "un eur",
        hh : "%d eur",
        d : "un devezh",
        dd : relativeTimeWithMutation,
        M : "ur miz",
        MM : relativeTimeWithMutation,
        y : "ur bloaz",
        yy : specialMutationForYears
    },
    ordinal : function (number) {
        var output = (number === 1) ? 'añ' : 'vet';
        return number + output;
    },
    week : {
        dow : 1, // Monday is the first day of the week.
        doy : 4  // The week that contains Jan 4th is the first week of the year.
    }
});
<commit_msg>Fixing indentation<commit_after>// moment.js language configuration
// language : breton (br)
// author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou

function relativeTimeWithMutation(number, withoutSuffix, key) {
    var format = {
        'mm': "munutenn",
        'MM': "miz",
        'dd': "devezh"
    };
    return number + ' ' + mutation(format[key], number);
}

function specialMutationForYears(number) {
    switch (lastNumber(number)) {
    case 1:
    case 3:
    case 4:
    case 5:
    case 9:
        return number + ' bloaz';
    default:
        return number + ' vloaz';
    }
}

function lastNumber(number) {
    if (number > 9) {
        return lastNumber(number % 10);
    }
    return number;
}

function mutation(text, number) {
    if (number === 2) {
        return softMutation(text);
    }
    return text;
}

function softMutation(text) {
    var mutationTable = {
        'm': 'v',
        'b': 'v',
        'd': 'z'
    };
    if (mutationTable[text.charAt(0)] === undefined) {
        return text;
    }
    return mutationTable[text.charAt(0)] + text.substring(1);
}

require('../moment').lang('br', {
    months : "Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),
    monthsShort : "Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),
    weekdays : "Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"),
    weekdaysShort : "Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),
    weekdaysMin : "Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),
    longDateFormat : {
        LT : "h[e]mm A",
        L : "DD/MM/YYYY",
        LL : "D [a viz] MMMM YYYY",
        LLL : "D [a viz] MMMM YYYY LT",
        LLLL : "dddd, D [a viz] MMMM YYYY LT"
    },
    calendar : {
        sameDay : '[Hiziv da] LT',
        nextDay : '[Warc\'hoazh da] LT',
        nextWeek : 'dddd [da] LT',
        lastDay : '[Dec\'h da] LT',
        lastWeek : 'dddd [paset da] LT',
        sameElse : 'L'
    },
    relativeTime : {
        future : "a-benn %s",
        past : "%s 'zo",
        s : "un nebeud segondennoù",
        m : "ur vunutenn",
        mm : relativeTimeWithMutation,
        h : "un eur",
        hh : "%d eur",
        d : "un devezh",
        dd : relativeTimeWithMutation,
        M : "ur miz",
        MM : relativeTimeWithMutation,
        y : "ur bloaz",
        yy : specialMutationForYears
    },
    ordinal : function (number) {
        var output = (number === 1) ? 'añ' : 'vet';
        return number + output;
    },
    week : {
        dow : 1, // Monday is the first day of the week.
        doy : 4  // The week that contains Jan 4th is the first week of the year.
    }
});
<|endoftext|>


<commit_before>// moment.js language configuration
// language : polish (pl)
// author : Rafal Hirsz : https://github.com/evoL
(function () {
    var plural = function (n) {
        return (n % 10 < 5) && (n % 10 > 1) && (~~(n / 10) !== 1);
    },

    translate = function (number, withoutSuffix, key) {
        var result = number + " ";
        switch (key) {
        case 'm':
            return withoutSuffix ? 'minuta' : 'minutę';
        case 'mm':
            return result + (plural(number) ? 'minuty' : 'minut');
        case 'h':
            return withoutSuffix  ? 'godzina'  : 'godzinę';
        case 'hh':
            return result + (plural(number) ? 'godziny' : 'godzin');
        case 'MM':
            return result + (plural(number) ? 'miesiące' : 'miesięcy');
        case 'yy':
            return result + (plural(number) ? 'lata' : 'lat');
        }
    },

    lang = {
        months : "styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),
        monthsShort : "sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),
        weekdays : "niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),
        weekdaysShort : "nie_pon_wt_śr_czw_pt_sb".split("_"),
        weekdaysMin : "N_Pn_Wt_Śr_Cz_Pt_So".split("_"),
        longDateFormat : {
            LT : "HH:mm",
            L : "DD-MM-YYYY",
            LL : "D MMMM YYYY",
            LLL : "D MMMM YYYY LT",
            LLLL : "dddd, D MMMM YYYY LT"
        },
        calendar : {
            sameDay: '[Dziś o] LT',
            nextDay: '[Jutro o] LT',
            nextWeek: '[W] dddd [o] LT',
            lastDay: '[Wczoraj o] LT',
            lastWeek: function () {
                switch (this.day()) {
                    case 0: return '[W zeszłą niedzielę o] LT'; break;
                    case 3: return '[W zeszłą środę o] LT'; break;
                    case 6: return '[W zeszłą sobotę o] LT'; break;
                    default: return '[W zeszły] dddd [o] LT';
                }
            },
            sameElse: 'L'
        },
        relativeTime : {
            future : "za %s",
            past : "%s temu",
            s : "kilka sekund",
            m : translate,
            mm : translate,
            h : translate,
            hh : translate,
            d : "1 dzień",
            dd : '%d dni',
            M : "miesiąc",
            MM : translate,
            y : "rok",
            yy : translate
        },
        ordinal : function (number) {
            return '.';
        }
    };

    // Node
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = lang;
    }
    // Browser
    if (typeof window !== 'undefined' && this.moment && this.moment.lang) {
        this.moment.lang('pl', lang);
    }
}());<commit_msg>Make jshint happy<commit_after>// moment.js language configuration
// language : polish (pl)
// author : Rafal Hirsz : https://github.com/evoL
(function () {
    var plural = function (n) {
        return (n % 10 < 5) && (n % 10 > 1) && (~~(n / 10) !== 1);
    },

        translate = function (number, withoutSuffix, key) {
            var result = number + " ";
            switch (key) {
            case 'm':
                return withoutSuffix ? 'minuta' : 'minutę';
            case 'mm':
                return result + (plural(number) ? 'minuty' : 'minut');
            case 'h':
                return withoutSuffix  ? 'godzina'  : 'godzinę';
            case 'hh':
                return result + (plural(number) ? 'godziny' : 'godzin');
            case 'MM':
                return result + (plural(number) ? 'miesiące' : 'miesięcy');
            case 'yy':
                return result + (plural(number) ? 'lata' : 'lat');
            }
        },

        lang = {
            months : "styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),
            monthsShort : "sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),
            weekdays : "niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),
            weekdaysShort : "nie_pon_wt_śr_czw_pt_sb".split("_"),
            weekdaysMin : "N_Pn_Wt_Śr_Cz_Pt_So".split("_"),
            longDateFormat : {
                LT : "HH:mm",
                L : "DD-MM-YYYY",
                LL : "D MMMM YYYY",
                LLL : "D MMMM YYYY LT",
                LLLL : "dddd, D MMMM YYYY LT"
            },
            calendar : {
                sameDay: '[Dziś o] LT',
                nextDay: '[Jutro o] LT',
                nextWeek: '[W] dddd [o] LT',
                lastDay: '[Wczoraj o] LT',
                lastWeek: function () {
                    switch (this.day()) {
                    case 0:
                        return '[W zeszłą niedzielę o] LT';
                    case 3:
                        return '[W zeszłą środę o] LT';
                    case 6:
                        return '[W zeszłą sobotę o] LT';
                    default:
                        return '[W zeszły] dddd [o] LT';
                    }
                },
                sameElse: 'L'
            },
            relativeTime : {
                future : "za %s",
                past : "%s temu",
                s : "kilka sekund",
                m : translate,
                mm : translate,
                h : translate,
                hh : translate,
                d : "1 dzień",
                dd : '%d dni',
                M : "miesiąc",
                MM : translate,
                y : "rok",
                yy : translate
            },
            ordinal : function (number) {
                return '.';
            }
        };

    // Node
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = lang;
    }
    // Browser
    if (typeof window !== 'undefined' && this.moment && this.moment.lang) {
        this.moment.lang('pl', lang);
    }
}());
<|endoftext|>


<commit_before>// This file is generated, do not edit.
module.exports = {
  "availableEvents": [
    "SwitchScenes",
    "ScenesChanged",
    "SceneCollectionChanged",
    "SceneCollectionListChanged",
    "SwitchTransition",
    "TransitionListChanged",
    "TransitionDurationChanged",
    "TransitionBegin",
    "ProfileChanged",
    "ProfileListChanged",
    "StreamStarting",
    "StreamStarted",
    "StreamStopping",
    "StreamStopped",
    "StreamStatus",
    "RecordingStarting",
    "RecordingStarted",
    "RecordingStopping",
    "RecordingStopped",
    "ReplayStarting",
    "ReplayStarted",
    "ReplayStopping",
    "ReplayStopped",
    "Exiting",
    "Heartbeat",
    "SourceOrderChanged",
    "SceneItemAdded",
    "SceneItemRemoved",
    "SceneItemVisibilityChanged",
    "PreviewSceneChanged",
    "StudioModeSwitched"
  ],
  "availableMethods": [
    "GetVersion",
    "GetAuthRequired",
    "Authenticate",
    "SetHeartbeat",
    "SetCurrentScene",
    "GetCurrentScene",
    "GetSceneList",
    "SetSourceRender",
    "SetVolume",
    "GetVolume",
    "ToggleMute",
    "SetMute",
    "GetMute",
    "SetSyncOffset",
    "GetSyncOffset",
    "SetSceneItemPosition",
    "SetSceneItemTransform",
    "SetSceneItemCrop",
    "GetSceneItemProperties",
    "SetSceneItemProperties",
    "GetTextGDIPlusProperties",
    "SetTextGDIPlusProperties",
    "GetBrowserSourceProperties",
    "SetBrowserSourceProperties",
    "ResetSceneItem",
    "GetSourcesList",
    "GetSourceSettings",
    "SetSourceSettings",
    "GetStreamingStatus",
    "StartStopStreaming",
    "StartStreaming",
    "StopStreaming",
    "StartStopRecording",
    "StartRecording",
    "StopRecording",
    "SetRecordingFolder",
    "GetRecordingFolder",
    "StartStopReplayBuffer",
    "StartReplayBuffer",
    "StopReplayBuffer",
    "SaveReplayBuffer",
    "GetTransitionList",
    "GetCurrentTransition",
    "SetCurrentTransition",
    "SetTransitionDuration",
    "GetTransitionDuration",
    "SetCurrentSceneCollection",
    "GetCurrentSceneCollection",
    "SetCurrentProfile",
    "GetCurrentProfile",
    "ListProfiles",
    "SetStreamSettings",
    "GetStreamSettings",
    "SaveStreamSettings",
    "GetStudioModeStatus",
    "GetPreviewScene",
    "SetPreviewScene",
    "TransitionToProgram",
    "EnableStudioMode",
    "DisableStudioMode",
    "ToggleStudioMode",
    "GetSpecialSources"
  ]
}<commit_msg>api(ci): Update available obs-websocket methods<commit_after>// This file is generated, do not edit.
module.exports = {
  "availableEvents": [
    "SwitchScenes",
    "ScenesChanged",
    "SceneCollectionChanged",
    "SceneCollectionListChanged",
    "SwitchTransition",
    "TransitionListChanged",
    "TransitionDurationChanged",
    "TransitionBegin",
    "ProfileChanged",
    "ProfileListChanged",
    "StreamStarting",
    "StreamStarted",
    "StreamStopping",
    "StreamStopped",
    "StreamStatus",
    "RecordingStarting",
    "RecordingStarted",
    "RecordingStopping",
    "RecordingStopped",
    "ReplayStarting",
    "ReplayStarted",
    "ReplayStopping",
    "ReplayStopped",
    "Exiting",
    "Heartbeat",
    "SourceOrderChanged",
    "SceneItemAdded",
    "SceneItemRemoved",
    "SceneItemVisibilityChanged",
    "PreviewSceneChanged",
    "StudioModeSwitched"
  ],
  "availableMethods": [
    "GetVersion",
    "GetAuthRequired",
    "Authenticate",
    "SetHeartbeat",
    "SetCurrentProfile",
    "GetCurrentProfile",
    "ListProfiles",
    "StartStopRecording",
    "StartRecording",
    "StopRecording",
    "SetRecordingFolder",
    "GetRecordingFolder",
    "StartStopReplayBuffer",
    "StartReplayBuffer",
    "StopReplayBuffer",
    "SaveReplayBuffer",
    "SetCurrentSceneCollection",
    "GetCurrentSceneCollection",
    "ListSceneCollections",
    "GetSceneItemProperties",
    "SetSceneItemProperties",
    "ResetSceneItem",
    "SetSceneItemRender",
    "SetSceneItemPosition",
    "SetSceneItemTransform",
    "SetSceneItemCrop",
    "SetCurrentScene",
    "GetCurrentScene",
    "GetSceneList",
    "GetSourcesList",
    "GetSourcesTypesList",
    "GetVolume",
    "SetVolume",
    "GetMute",
    "SetMute",
    "ToggleMute",
    "SetSyncOffset",
    "GetSyncOffset",
    "GetSourceSettings",
    "SetSourceSettings",
    "GetTextGDIPlusProperties",
    "SetTextGDIPlusProperties",
    "GetBrowserSourceProperties",
    "SetBrowserSourceProperties",
    "GetSpecialSources",
    "GetStreamingStatus",
    "StartStopStreaming",
    "StartStreaming",
    "StopStreaming",
    "SetStreamSettings",
    "GetStreamSettings",
    "SaveStreamSettings",
    "GetStudioModeStatus",
    "GetPreviewScene",
    "SetPreviewScene",
    "TransitionToProgram",
    "EnableStudioMode",
    "DisableStudioMode",
    "ToggleStudioMode",
    "GetTransitionList",
    "GetCurrentTransition",
    "SetCurrentTransition",
    "SetTransitionDuration",
    "GetTransitionDuration"
  ]
}<|endoftext|>


<commit_before>'use strict';
var path = require('path')
  , glob = require('glob')
  , winston = require('winston')
  , ObjectManage = require('object-manage')
  , async = require('async')
  , events = require('events')
  , emitter

/**
 * Setup Event Emitter
 * @type {EventEmitter}
 */
module.exports = emitter = new events.EventEmitter()

/**
 * Main constructor sets options
 * @param options
 */
var Apx = function(options){
  var that = this
  that.sysLog = require('./SysLog')
  //set syslog level now
  if('production' === process.env.NODE_ENV){
    that.sysLog.level = 1
  }
  if(options.sysLogLevel) that.sysLog.level = options.sysLogLevel
  that.sysLog.info('Welcome to APX')
  //freshen
  that.initializers = []
  that.log = null
  that.services = []
  that.tasks = []
  that.translators = []
  //load config
  that.sysLog.info('Loading configuration options')
  options = options || {}
  that.config = new ObjectManage(that.configSchema)
  //load runtime config first and last (config files in the middle)
  that.config.load(options)
  //load additional config
  that.loadItems(
    that.config.get('config'),
    function(config,next){
      that.config.load(config)
      next()
    },
    //finish init here so we know the config is loaded fully
    function(){
      //pass rest of the options to config
      //  so that runtime options are kept over all else
      that.config.load(options)
      that.sysLog.info('Config loading complete')
      //update ready state
      that.readyState = 2
      emitter.emit('readyStateChange',that.readyState)
    }
  )
}

/**
 * Ready state of APX
 *  0 - Dead
 *  1 - Ready
 *  2 - Configured
 *  3 - Starting
 *  4 - Stopping
 * @type {Number}
 */
Apx.prototype.readyState = 0

/**
 * Registered initializers
 * @type {Array}
 */
Apx.prototype.initializers = []

/**
 * Winston logging instance
 * @type {null}
 */
Apx.prototype.log = null

/**
 * Instantiated services
 * @type {Array}
 */
Apx.prototype.services = []

/**
 * Registered tasks
 * @type {Array}
 */
Apx.prototype.tasks = []

/**
 * Registered translators
 * @type {Array}
 */
Apx.prototype.translators = []

/**
 * Request object
 * @type {Function}
 */
Apx.prototype.Request = require('./Request')

/**
 * Response Object
 * @type {Function}
 */
Apx.prototype.Response = require('./Response')

/**
 * Initial config schema
 * @type {*}
 */
Apx.prototype.configSchema = {
  testing: false,
  cwd: '',
  tasks: [],
  translators: [],
  initializers: [],
  winston: {
    console: true,
    file: ''
  }
}

/**
 * Load additional config properties
 */
Apx.prototype.load = function(options){
  this.config.load(options)
}

/**
 * Worker init
 */
Apx.prototype.start = function(){
  var that = this
  //update ready state
  that.readyState = 3
  emitter.emit('readyStateChange',that.readyState)
  //send welcome message
  that.sysLog.info('Hi! Beginning APX initialization and startup')
  that.sysLog.info('Setting up log transports')
  //setup logging
  var logTransports = []
  if(that.config.get('winston.console')){
    logTransports.push(new (winston.transports.Console)())
  }
  if(that.config.get('winston.file')){
    logTransports.push(new (winston.transports.File)({
      filename: that.config.get('winston.file')
    }))
  }
  if(that.config.get('testing')){
    require('winston-memory').Memory()
    logTransports = [new (winston.transports.Memory)()]
  }
  that.log = new (winston.Logger)({
    transports: logTransports
  })
  that.sysLog.info('Winston logger now active, will continue to sysLog init messages')

  //setup loaders so we can run them async (which is async of async)
  var loaders = [
    //load initializers
    function(finish){
      if(that.config.get('initializers')){
        that.sysLog.info('Starting to load initializers')
        that.loadItems(
          that.config.get('initializers'),
          function(initializer,next){
            that.initializers[initializer.name] = initializer.start(that,next)
          },
          function(err){
            that.sysLog.info('Finished loading initializers')
            finish(err)
          }
        )
      }

    },
    //load translators
    function(finish){
      if(that.config.get('translators')){
        that.sysLog.info('Starting to load translators')
        that.loadItems(
          that.config.get('translators'),
          function(translator,next){
            that.translators[translator.name] = translator.start(that,next)
          },
          function(err){
            that.sysLog.info('Finished loading translators')
            finish(err)
          }
        )
      }

    }
  ]
  //fire the loaders
  async.eachSeries(
    loaders,
    function(loader,next){
      loader(next)
    },
    function(err){
      //emit error event if there are errors
      if(err){
        emitter.emit('error',err)
      }
      //call the ready function
      that.sysLog.info('APX system startup complete')
      //update ready state
      that.readyState = 1
      emitter.emit('readyStateChange',that.readyState)
      //fire ready event
      emitter.emit('ready',that)
    }
  )

}

/**
 * Destruct the APX instance and shutdown translators
 */
Apx.prototype.stop = function(){
  var that = this
  //update ready state
  that.readyState = 4
  emitter.emit('readyStateChange',that.readyState)
  //inform user of shutdown
  that.sysLog.info('Starting to shutdown APX')
  //setup loaders so we can run them async (which is async of async)
  var loaders = [
    //load initializers
    function(finish){
      if(that.config.get('initializers')){
        that.sysLog.info('Starting to shutdown initializers')
        that.loadItems(
          that.config.get('initializers'),
          function(initializer,next){
            that.initializers[initializer.name] = initializer.stop(that,next)
          },
          function(err){
            that.sysLog.info('Finished shutting down initializers')
            finish(err)
          }
        )
      }
    },
    //load translators
    function(finish){
      if(that.config.get('translators')){
        that.sysLog.info('Starting to shutdown translators')
        that.loadItems(
          that.config.get('translators'),
          function(translator,next){
            that.translators[translator.name] = translator.stop(that,next)
          },
          function(err){
            that.sysLog.info('Finished shutting down translators')
            finish(err)
          }
        )
      }

    }
  ]
  //fire the loaders
  async.eachSeries(
    loaders,
    function(loader,next){
      loader(next)
    },
    function(err){
      //emit error event if there are errors
      if(err){
        emitter.emit('error',err)
      }
      //call the ready function
      that.sysLog.info('APX system shutdown complete')
      //update ready stats
      that.readyState = 0
      emitter.emit('readyStateChange',that.readyState)
      //fire dead event
      emitter.emit('dead',that)
    }
  )
}

/**
 * Resolve path with CWD
 * @param file
 * @returns {*}
 */
Apx.prototype.resolvePath = function(file){
  var rv = file
    , cwd = this.config.get('cwd')
  if(cwd)
    rv = cwd + '/' + file
  return path.resolve(rv)
}

/**
 * Will take an array of items/globs and
 * load them against the environment and will
 * fire iterator with the item object on success
 * @param items
 * @param iterator
 * @param done
 */
Apx.prototype.loadItems = function(items,iterator,done){
  if(undefined === items) done()
  else {
    var that = this
      , cwd = path.resolve(that.config.get('cwd'))
    if('string' === typeof items) items = [items]
    //do all of this async
    async.each(
      items,
      function(item,finish){
        //try to load a package name natively
        if(-1 === item.indexOf('/') && -1 === item.indexOf('\\') && -1 === item.indexOf('.js')){
          that.sysLog.info('Loading package (' + item + ')')
          iterator(require(item),finish)
          //try to resolve the path with glob
        } else {
          glob(item,{cwd: cwd},function(err,files){
            if(err) throw err
            async.each(
              files,
              function(file,next){
                that.sysLog.info('Loading package from path (' + that.resolvePath(file) + ')')
                iterator(require(that.resolvePath(file)),next)
              },
              finish
            )
          })
        }
      },
      done
    )
  }
}

/**
 * Run APX action
 *  Should be called by a translator
 * @param action  Can be an action or a filename to require
 * @param req
 * @param method
 * @param fn
 */
Apx.prototype.runAction = function(action,req,method,fn){
  if('function' === typeof method){
    fn = method
    method = 'run'
  }
  if('string' === typeof action) action = require(this.resolvePath(action))
  if(false === (req instanceof this.Request))
    req = new this.Request(req)
  this.sysLog.info('Running action ' + action.name + ' : ' + method)
  //run action
  emitter.emit('runActionBefore',action)
  action[method](this,req,new this.Response(fn))
  emitter.emit('runActionAfter',action)
}

/**
 * Run APX task
 * @param task  Can be a task or a filename to require
 * @param req
 * @param done
 */
Apx.prototype.runTask = function(task,req,done){
  if('string' === typeof task) task = require(this.resolvePath(task))
  if(false === (req instanceof this.Request))
    req = new this.Request(req)
  this.sysLog.info('Running task ' + task.name)
  //run task
  emitter.emit('runTaskBefore',task)
  task.run(this,req,done)
  emitter.emit('runTaskAfter',task)
}

/**
 * Returns new service instance
 * @param service  Can be a service or a filename to require
 * @returns {service}
 */
Apx.prototype.newService = function(service){
  if('string' === typeof service) service = require(this.resolvePath(service))
  this.sysLog.info('Returning new service instance for ' + service.name)
  return new service.module()
}

/**
 * Obtain service instance
 *  manages singletons
 * @param service  Can be a service or a filename to require
 * @returns {*}
 */
Apx.prototype.service = function(service){
  if('string' === typeof service) service = require(this.resolvePath(service))
  if(!this.services[service.name]){
    this.services[service.name] = new service.module()
  }
  this.sysLog.info('Returning service instance for ' + service.name)
  return this.services[service.name]
}

/**
 * Operation of APX library
 *   Implements standard daemon operators (start/stop)
 */
var instance
module.exports.setup = function(opts){
  instance = new Apx(opts)
}
module.exports.start = function(){
  instance.start()
}
module.exports.stop = function(){
  instance.stop()
  emitter.on('stop',function(){
    instance = undefined
  })
}
module.exports.Apx = Apx
<commit_msg>Change that to self<commit_after>'use strict';
var path = require('path')
  , glob = require('glob')
  , winston = require('winston')
  , ObjectManage = require('object-manage')
  , async = require('async')
  , events = require('events')
  , emitter

/**
 * Setup Event Emitter
 * @type {EventEmitter}
 */
module.exports = emitter = new events.EventEmitter()

/**
 * Main constructor sets options
 * @param options
 */
var Apx = function(options){
  var self = this
  self.sysLog = require('./SysLog')
  //set syslog level now
  if('production' === process.env.NODE_ENV){
    self.sysLog.level = 1
  }
  if(options.sysLogLevel) self.sysLog.level = options.sysLogLevel
  self.sysLog.info('Welcome to APX')
  //freshen
  self.initializers = []
  self.log = null
  self.services = []
  self.tasks = []
  self.translators = []
  //load config
  self.sysLog.info('Loading configuration options')
  options = options || {}
  self.config = new ObjectManage(self.configSchema)
  //load runtime config first and last (config files in the middle)
  self.config.load(options)
  //load additional config
  self.loadItems(
    self.config.get('config'),
    function(config,next){
      self.config.load(config)
      next()
    },
    //finish init here so we know the config is loaded fully
    function(){
      //pass rest of the options to config
      //  so that runtime options are kept over all else
      self.config.load(options)
      self.sysLog.info('Config loading complete')
      //update ready state
      self.readyState = 2
      emitter.emit('readyStateChange',self.readyState)
    }
  )
}

/**
 * Ready state of APX
 *  0 - Dead
 *  1 - Ready
 *  2 - Configured
 *  3 - Starting
 *  4 - Stopping
 * @type {Number}
 */
Apx.prototype.readyState = 0

/**
 * Registered initializers
 * @type {Array}
 */
Apx.prototype.initializers = []

/**
 * Winston logging instance
 * @type {null}
 */
Apx.prototype.log = null

/**
 * Instantiated services
 * @type {Array}
 */
Apx.prototype.services = []

/**
 * Registered tasks
 * @type {Array}
 */
Apx.prototype.tasks = []

/**
 * Registered translators
 * @type {Array}
 */
Apx.prototype.translators = []

/**
 * Request object
 * @type {Function}
 */
Apx.prototype.Request = require('./Request')

/**
 * Response Object
 * @type {Function}
 */
Apx.prototype.Response = require('./Response')

/**
 * Initial config schema
 * @type {*}
 */
Apx.prototype.configSchema = {
  testing: false,
  cwd: '',
  tasks: [],
  translators: [],
  initializers: [],
  winston: {
    console: true,
    file: ''
  }
}

/**
 * Load additional config properties
 */
Apx.prototype.load = function(options){
  this.config.load(options)
}

/**
 * Worker init
 */
Apx.prototype.start = function(){
  var self = this
  //update ready state
  self.readyState = 3
  emitter.emit('readyStateChange',self.readyState)
  //send welcome message
  self.sysLog.info('Hi! Beginning APX initialization and startup')
  self.sysLog.info('Setting up log transports')
  //setup logging
  var logTransports = []
  if(self.config.get('winston.console')){
    logTransports.push(new (winston.transports.Console)())
  }
  if(self.config.get('winston.file')){
    logTransports.push(new (winston.transports.File)({
      filename: self.config.get('winston.file')
    }))
  }
  if(self.config.get('testing')){
    require('winston-memory').Memory()
    logTransports = [new (winston.transports.Memory)()]
  }
  self.log = new (winston.Logger)({
    transports: logTransports
  })
  self.sysLog.info('Winston logger now active, will continue to sysLog init messages')

  //setup loaders so we can run them async (which is async of async)
  var loaders = [
    //load initializers
    function(finish){
      if(self.config.get('initializers')){
        self.sysLog.info('Starting to load initializers')
        self.loadItems(
          self.config.get('initializers'),
          function(initializer,next){
            self.initializers[initializer.name] = initializer.start(self,next)
          },
          function(err){
            self.sysLog.info('Finished loading initializers')
            finish(err)
          }
        )
      }

    },
    //load translators
    function(finish){
      if(self.config.get('translators')){
        self.sysLog.info('Starting to load translators')
        self.loadItems(
          self.config.get('translators'),
          function(translator,next){
            self.translators[translator.name] = translator.start(self,next)
          },
          function(err){
            self.sysLog.info('Finished loading translators')
            finish(err)
          }
        )
      }

    }
  ]
  //fire the loaders
  async.eachSeries(
    loaders,
    function(loader,next){
      loader(next)
    },
    function(err){
      //emit error event if there are errors
      if(err){
        emitter.emit('error',err)
      }
      //call the ready function
      self.sysLog.info('APX system startup complete')
      //update ready state
      self.readyState = 1
      emitter.emit('readyStateChange',self.readyState)
      //fire ready event
      emitter.emit('ready',self)
    }
  )

}

/**
 * Destruct the APX instance and shutdown translators
 */
Apx.prototype.stop = function(){
  var self = this
  //update ready state
  self.readyState = 4
  emitter.emit('readyStateChange',self.readyState)
  //inform user of shutdown
  self.sysLog.info('Starting to shutdown APX')
  //setup loaders so we can run them async (which is async of async)
  var loaders = [
    //load initializers
    function(finish){
      if(self.config.get('initializers')){
        self.sysLog.info('Starting to shutdown initializers')
        self.loadItems(
          self.config.get('initializers'),
          function(initializer,next){
            self.initializers[initializer.name] = initializer.stop(self,next)
          },
          function(err){
            self.sysLog.info('Finished shutting down initializers')
            finish(err)
          }
        )
      }
    },
    //load translators
    function(finish){
      if(self.config.get('translators')){
        self.sysLog.info('Starting to shutdown translators')
        self.loadItems(
          self.config.get('translators'),
          function(translator,next){
            self.translators[translator.name] = translator.stop(self,next)
          },
          function(err){
            self.sysLog.info('Finished shutting down translators')
            finish(err)
          }
        )
      }

    }
  ]
  //fire the loaders
  async.eachSeries(
    loaders,
    function(loader,next){
      loader(next)
    },
    function(err){
      //emit error event if there are errors
      if(err){
        emitter.emit('error',err)
      }
      //call the ready function
      self.sysLog.info('APX system shutdown complete')
      //update ready stats
      self.readyState = 0
      emitter.emit('readyStateChange',self.readyState)
      //fire dead event
      emitter.emit('dead',self)
    }
  )
}

/**
 * Resolve path with CWD
 * @param file
 * @returns {*}
 */
Apx.prototype.resolvePath = function(file){
  var rv = file
    , cwd = this.config.get('cwd')
  if(cwd)
    rv = cwd + '/' + file
  return path.resolve(rv)
}

/**
 * Will take an array of items/globs and
 * load them against the environment and will
 * fire iterator with the item object on success
 * @param items
 * @param iterator
 * @param done
 */
Apx.prototype.loadItems = function(items,iterator,done){
  if(undefined === items) done()
  else {
    var self = this
      , cwd = path.resolve(self.config.get('cwd'))
    if('string' === typeof items) items = [items]
    //do all of this async
    async.each(
      items,
      function(item,finish){
        //try to load a package name natively
        if(-1 === item.indexOf('/') && -1 === item.indexOf('\\') && -1 === item.indexOf('.js')){
          self.sysLog.info('Loading package (' + item + ')')
          iterator(require(item),finish)
          //try to resolve the path with glob
        } else {
          glob(item,{cwd: cwd},function(err,files){
            if(err) throw err
            async.each(
              files,
              function(file,next){
                self.sysLog.info('Loading package from path (' + self.resolvePath(file) + ')')
                iterator(require(self.resolvePath(file)),next)
              },
              finish
            )
          })
        }
      },
      done
    )
  }
}

/**
 * Run APX action
 *  Should be called by a translator
 * @param action  Can be an action or a filename to require
 * @param req
 * @param method
 * @param fn
 */
Apx.prototype.runAction = function(action,req,method,fn){
  if('function' === typeof method){
    fn = method
    method = 'run'
  }
  if('string' === typeof action) action = require(this.resolvePath(action))
  if(false === (req instanceof this.Request))
    req = new this.Request(req)
  this.sysLog.info('Running action ' + action.name + ' : ' + method)
  //run action
  emitter.emit('runActionBefore',action)
  action[method](this,req,new this.Response(fn))
  emitter.emit('runActionAfter',action)
}

/**
 * Run APX task
 * @param task  Can be a task or a filename to require
 * @param req
 * @param done
 */
Apx.prototype.runTask = function(task,req,done){
  if('string' === typeof task) task = require(this.resolvePath(task))
  if(false === (req instanceof this.Request))
    req = new this.Request(req)
  this.sysLog.info('Running task ' + task.name)
  //run task
  emitter.emit('runTaskBefore',task)
  task.run(this,req,done)
  emitter.emit('runTaskAfter',task)
}

/**
 * Returns new service instance
 * @param service  Can be a service or a filename to require
 * @returns {service}
 */
Apx.prototype.newService = function(service){
  if('string' === typeof service) service = require(this.resolvePath(service))
  this.sysLog.info('Returning new service instance for ' + service.name)
  return new service.module()
}

/**
 * Obtain service instance
 *  manages singletons
 * @param service  Can be a service or a filename to require
 * @returns {*}
 */
Apx.prototype.service = function(service){
  if('string' === typeof service) service = require(this.resolvePath(service))
  if(!this.services[service.name]){
    this.services[service.name] = new service.module()
  }
  this.sysLog.info('Returning service instance for ' + service.name)
  return this.services[service.name]
}

/**
 * Operation of APX library
 *   Implements standard daemon operators (start/stop)
 */
var instance
module.exports.setup = function(opts){
  instance = new Apx(opts)
}
module.exports.start = function(){
  instance.start()
}
module.exports.stop = function(){
  instance.stop()
  emitter.on('stop',function(){
    instance = undefined
  })
}
module.exports.Apx = Apx
<|endoftext|>


<commit_before>// @name: Xxx.js

(function(global) {

// --- variable --------------------------------------------
var _inNode = "process" in global;

// --- define ----------------------------------------------
// --- interface -------------------------------------------
function Xxx() { // @help: Xxx
}
Xxx["name"] = "Xxx";
Xxx["repository"] = "https://github.com/uupaa/Xxx.js";

// --- implement -------------------------------------------

//{@assert
function _type(value, types, keys) {
    return types.split(/[\|\/]/).some(judge);

    function judge(type) {
        switch (type.toLowerCase()) {
        case "omit":    return value === undefined || value === null;
        case "array":   return Array.isArray(value);
        case "integer": return typeof value === "number" && Math.ceil(value) === value;
        case "object":  return (keys && value && !hasKeys(value, keys)) ? false
                             : (value || 0).constructor === ({}).constructor;
        default:        return Object.prototype.toString.call(value) === "[object " + type + "]";
        }
    }
    function hasKeys(value, keys) {
        var ary = keys ? keys.split(",") : null;

        return Object.keys(value).every(function(key) {
            return ary.indexOf(key) >= 0;
        });
    }
}
function _if(value, msg) {
    if (value) {
        throw new Error(msg);
    }
}
//}@assert

// --- export ----------------------------------------------
//{@node
if (_inNode) {
    module["exports"] = Xxx;
}
//}@node
if (global["Xxx"]) {
    global["Xxx_"] = Xxx; // already exsists
} else {
    global["Xxx"]  = Xxx;
}

})(this.self || global);

<commit_msg>add 'use strict'<commit_after>// @name: Xxx.js

(function(global) {
"use strict";

// --- variable --------------------------------------------
var _inNode = "process" in global;
var _inWorker = "WorkerLocation" in global;
var _inBrowser = "self" in global;

// --- define ----------------------------------------------
// --- interface -------------------------------------------
function Xxx() { // @help: Xxx
}
Xxx["name"] = "Xxx";
Xxx["repository"] = "https://github.com/uupaa/Xxx.js";

// --- implement -------------------------------------------

//{@assert
function _type(value, types, keys) {
    return types.split(/[\|\/]/).some(judge);

    function judge(type) {
        switch (type.toLowerCase()) {
        case "omit":    return value === undefined || value === null;
        case "array":   return Array.isArray(value);
        case "integer": return typeof value === "number" && Math.ceil(value) === value;
        case "object":  return (keys && value && !hasKeys(value, keys)) ? false
                             : (value || 0).constructor === ({}).constructor;
        default:        return Object.prototype.toString.call(value) === "[object " + type + "]";
        }
    }
    function hasKeys(value, keys) {
        var ary = keys ? keys.split(",") : null;

        return Object.keys(value).every(function(key) {
            return ary.indexOf(key) >= 0;
        });
    }
}
function _if(value, msg) {
    if (value) {
        throw new Error(msg);
    }
}
//}@assert

// --- export ----------------------------------------------
//{@node
if (_inNode) {
    module["exports"] = Xxx;
}
//}@node
if (global["Xxx"]) {
    global["Xxx_"] = Xxx; // already exsists
} else {
    global["Xxx"]  = Xxx;
}

})(this.self || global);

<|endoftext|>


<commit_before>/**
 * We.js ACL code
 */

// libs
var async = require('async');
var _ = require('lodash');

// acl object how by default are avaible in sails.acl
var acl = {};

// -- memory caches
// variable to store roles
acl.roles = {};
// variable to store permissions
acl.permissions = {};

acl.lowerCasePermissionNames = {};

/**
 * Init function, use after start waterline
 *
 * @param  {object} sails
 * @param  {Function} cb callback
 */
acl.init = function initAcl (sails, cb) {
  // load roles and permissions in parallel
  async.parallel([
    function loadPermissions(done) {
      acl.loadAllPermissions(sails, function(err, permissions) {
        if (err) return done(err);
        acl.permissions = permissions;
        done();
      })
     },
    function loadRoles(done) {
      acl.loadAllRoles(sails, function(err, roles) {
        if (err) return done(err);
        acl.roles = roles;
        done();
      })
    }
  ], function(err) {
    if (err) return cb(err);
    // ensures that default roles are avaible
    acl.createDefaultRolesIfNotExists(sails, function(err) {
      if (err) return cb(err);
      cb();
    })
  });
}

acl.resolvePermissionName = function resolvePermissionName(permissionName) {
  if (!acl.permissions[permissionName] && (acl.lowerCasePermissionNames[permissionName]) ) {
    return acl.lowerCasePermissionNames[permissionName];
  }
  return permissionName;
},

/**
 * Function to check if one user has some permission
 *
 * @param  {string} permissionName
 * @param  {object} user
 * @param  {Function} callback
 */
acl.can = function userCan(permissionName, user, record, callback) {
  var can = false;

  // return false if this permission dont have roles
  if (
    !acl.permissions[permissionName] ||
    !acl.permissions[permissionName].roles ||
    acl.permissions[permissionName].roles.length < 1
  ) {
    return callback(null, can);
  }

  var context = acl.getUserContext(user, record);
  var roles = context.roles;

  var permission = acl.permissions[permissionName];

  for (var i = permission.roles.length - 1; i >= 0; i--) {
    if ( roles.indexOf(permission.roles[i].name) >= 0 ) {
      can = true;
      break;
    }
  }

  callback(null, can);
}

/**
 * get context for user
 *
 * @param  {object} user
 * @param  {object} record
 * @return {object} context
 */
acl.getUserContext = function(user, record) {
  var context = {
    roles: []
  };

  if (!user) {
    // - unAuthenticated roles
    context.roles.push('unAuthenticated');
    return context;
  } else if(user._context) {
    context =  user._context;
  } else {
    // - authenticated roles
    if ( user.roles ) {
      context.roles = user.roles;
    }
    context.roles.push('authenticated');
  }

  // - check if current user have the owner roles
  if ( record && record.creator) {
    var creatorId;
    if (_.isObject(record.creator)) {
      creatorId = record.creator.id;
    } else {
      creatorId = record.creator;
    }
    if (user.id == creatorId) context.roles.push('owner');
  }
  // save one cache
  user._context = context;
  return context;
}

/**
 * Can sails policy
 * Run for every sails.js request
 *
 * @param  {object} req express request
 * @param  {Function} callback
 */
acl.canPolicy = function userCanPolicy(req, callback) {
  // config for enable or disable ACL
  if ( req._sails.config.acl.disabled ) return callback();
  // only check if has action and controller
  if (!req.options || !req.options.controller || !req.options.action)
    return callback(null, true);
  // if user is admin can do everything
  if (req.isAuthenticated() && req.user.isAdmin && req.user.active)
    return callback(null, true);

  if (acl.checkIfAclIsDisabledInController(req.options.controller, req._sails))
    return callback(null, true);

  var record;
  // get record from context - see sails-context
  if (req.context && req.context.record)
    record = req.context.record;

  var permissionName = acl.resolvePermissionName( req.options.controller + '_' + req.options.action );
  acl.can(permissionName, req.user, record, callback);
}

/**
 * Check if acl is disabled to this controller
 *
 * @param  {string} controllerName
 * @param  {object} sails
 * @return {boolean} true for disabled or false for enabled
 */
acl.checkIfAclIsDisabledInController = function checkIfAclIsDisabledInController(controllerName, sails) {
  if (
    sails.controllers[controllerName] &&
    sails.controllers[controllerName]._config &&
    sails.controllers[controllerName]._config.acl === false
  ){
    // is disabled
    return true;
  }
  // not is disabled
  return false;
}

/**
 * Load all permissions from database
 *
 * @param  {object} sails
 * @param  {Function} callback
 * @return {object} waterline find promisse
 */
acl.loadAllPermissions = function loadAllPermissionsFromDB(sails, cb) {
  return sails.models.permission.find()
  .limit( 400 )
  .populate('roles')
  .exec(function found(err, matchingRecords) {
    if (err) return cb(err);
    var permissions = {};
    for (var i = matchingRecords.length - 1; i >= 0; i--) {
      permissions[ matchingRecords[i].name ] = matchingRecords[i];
      // lower case caches
      acl.lowerCasePermissionNames[matchingRecords[i].name.toLowerCase()] = matchingRecords[i].name;
    }
    return cb(null, permissions);
  });
}

/**
 * Load all roles from DB
 * @param  {object} sails
 * @param  {Function} cb  callback
 * @return {object} waterline find promisse
 */
acl.loadAllRoles = function loadAllRolesFromDB(sails, cb) {
  return sails.models.role.find()
  .limit( 50 )
  .exec(function found(err, matchingRecords) {
    if (err) return cb(err);
    var roles = {};
    for (var i = matchingRecords.length - 1; i >= 0; i--) {
      roles[matchingRecords[i].name] = matchingRecords[i];
    }
    return cb(null, roles);
  });
}

/**
 * Get all acl permissions from sails.controllers
 * @param  {object} sails
 * @param  {Function} callback
 */
acl.getAllActionPermisons = function(sails, callback) {
  var controllers = sails.controllers;
  var permissions = {};

  var controllerNames = Object.keys(sails.controllers);

  async.each(controllerNames, function (controllerName, next) {
    // skip if disabled
    if (acl.checkIfAclIsDisabledInController(controllerName, sails))
      return next();

    if (acl.hasBlueprint(controllerName, sails)) {
      permissions[controllerName + '_' + 'find'] =
        acl.generatePermissionObject(controllerName, 'find');

      permissions[controllerName + '_' + 'findOne'] =
        acl.generatePermissionObject(controllerName, 'findOne');

      permissions[controllerName + '_' + 'create'] =
        acl.generatePermissionObject(controllerName, 'create');

      permissions[controllerName + '_' + 'update'] =
        acl.generatePermissionObject(controllerName, 'update');

      permissions[controllerName + '_' + 'add'] =
        acl.generatePermissionObject(controllerName, 'add');

      permissions[controllerName + '_' + 'remove'] =
        acl.generatePermissionObject(controllerName, 'remove');

      permissions[controllerName + '_' + 'delete'] =
        acl.generatePermissionObject(controllerName, 'delete');
    }

    var actionNames = Object.keys(controllers[controllerName]);
    for (var i = actionNames.length - 1; i >= 0; i--) {
      if( !_.isObject(controllers[controllerName][actionNames[i]]) )
        continue;

      permissions[controllerName + '_' + actionNames[i]] =
        acl.generatePermissionObject(controllerName, actionNames[i]);
    }

    next();
  }, function(err) {
    callback(err, permissions);
  })
}

acl.generatePermissionObject = function (controllerName, actionName) {
  return {
    controller: controllerName,
    action: actionName,
    name: controllerName + '_' + actionName
  }
}

acl.hasBlueprint = function(name, sails) {

  // check if rest blueprint is disabled
  if (sails.controllers[name] && sails.controllers[name]._config) {
    if(sails.controllers[name]._config === false) {
      return false;
    }
  }
  // check if has a model with same name as this controller
  if( !sails.models[name])
    return false;

  return true;
}

/**
 * Check and create default roles required for all we.js projects
 *
 * @param  {object} sails
 * @param  {Function} cb callback
 */
acl.createDefaultRolesIfNotExists = function createDefaultRolesIfNotExists(sails, cb) {
  async.parallel([
    function unAuthenticatedRole(done) {
      acl.registerOneDefaltRole(sails, 'unAuthenticated', done);
    },
    function authenticatedRole(done) {
      acl.registerOneDefaltRole(sails, 'authenticated', done);
    },
    function ownerRole(done) {
      acl.registerOneDefaltRole(sails, 'owner', done);
    },
    function administratorRole(done) {
      acl.registerOneDefaltRole(sails, 'administrator', done);
    }
  ], cb)
}

/**
 * Create and register one of the default roles if not exists
 *
 * @param  {object} sails
 * @param  {string} roleName
 * @param  {Function} done / callback
 *
 */
acl.registerOneDefaltRole = function registerOneDefaltRole(sails, roleName, done) {
  // skip if this default role already exists
  if ( acl.roles[roleName] )
    return done();
  acl.createRole(sails, {  name: roleName }, done);
}

/**
 * Create and register one role
 *
 * @param {object} sails.js object
 * @param  {object} data { name: 'roleName', ... } the new role object
 * @param  {Function} cb  callback
 * @return {[type]} waterline create query promisse
 */
acl.createRole = function createRole(sails, data, cb) {
  // if not exists create the role
  return sails.models.role.create(data)
  .exec(function (err, role) {
    if (err) return cb(err);
    // set the role in global roles how will be avaible in sails.acl.roles
    acl.roles[data.name] = role;
    cb();
  })
}

module.exports = acl;<commit_msg>add extra blocks for oauth forbiddens<commit_after>/**
 * We.js ACL code
 */

// libs
var async = require('async');
var _ = require('lodash');

// acl object how by default are avaible in sails.acl
var acl = {};

// -- memory caches
// variable to store roles
acl.roles = {};
// variable to store permissions
acl.permissions = {};

acl.lowerCasePermissionNames = {};

/**
 * Init function, use after start waterline
 *
 * @param  {object} sails
 * @param  {Function} cb callback
 */
acl.init = function initAcl (sails, cb) {
  // load roles and permissions in parallel
  async.parallel([
    function loadPermissions(done) {
      acl.loadAllPermissions(sails, function(err, permissions) {
        if (err) return done(err);
        acl.permissions = permissions;
        done();
      })
     },
    function loadRoles(done) {
      acl.loadAllRoles(sails, function(err, roles) {
        if (err) return done(err);
        acl.roles = roles;
        done();
      })
    }
  ], function(err) {
    if (err) return cb(err);
    // ensures that default roles are avaible
    acl.createDefaultRolesIfNotExists(sails, function(err) {
      if (err) return cb(err);
      cb();
    })
  });
}

acl.resolvePermissionName = function resolvePermissionName(permissionName) {
  if (!acl.permissions[permissionName] && (acl.lowerCasePermissionNames[permissionName]) ) {
    return acl.lowerCasePermissionNames[permissionName];
  }
  return permissionName;
},

/**
 * Function to check if one user has some permission
 *
 * @param  {string} permissionName
 * @param  {object} user
 * @param  {Function} callback
 */
acl.can = function userCan(permissionName, user, record, callback) {
  var can = false;

  // return false if this permission dont have roles
  if (
    !acl.permissions[permissionName] ||
    !acl.permissions[permissionName].roles ||
    acl.permissions[permissionName].roles.length < 1
  ) {
    return callback(null, can);
  }

  var context = acl.getUserContext(user, record);
  var roles = context.roles;

  var permission = acl.permissions[permissionName];

  for (var i = permission.roles.length - 1; i >= 0; i--) {
    if ( roles.indexOf(permission.roles[i].name) >= 0 ) {
      can = true;
      break;
    }
  }

  callback(null, can);
}

/**
 * get context for user
 *
 * @param  {object} user
 * @param  {object} record
 * @return {object} context
 */
acl.getUserContext = function(user, record) {
  var context = {
    roles: []
  };

  if (!user) {
    // - unAuthenticated roles
    context.roles.push('unAuthenticated');
    return context;
  } else if(user._context) {
    context =  user._context;
  } else {
    // - authenticated roles
    if ( user.roles ) {
      context.roles = user.roles;
    }
    context.roles.push('authenticated');
  }

  // - check if current user have the owner roles
  if ( record && record.creator) {
    var creatorId;
    if (_.isObject(record.creator)) {
      creatorId = record.creator.id;
    } else {
      creatorId = record.creator;
    }
    if (user.id == creatorId) context.roles.push('owner');
  }
  // save one cache
  user._context = context;
  return context;
}

/**
 * Can sails policy
 * Run for every sails.js request
 *
 * @param  {object} req express request
 * @param  {Function} callback
 */
acl.canPolicy = function userCanPolicy(req, callback) {
  // config for enable or disable ACL
  if ( req._sails.config.acl.disabled ) return callback();
  // only check if has action and controller
  if (!req.options || !req.options.controller || !req.options.action)
    return callback(null, true);
  // if user is admin can do everything
  if (req.isAuthenticated() && req.user.isAdmin && req.user.active)
    return callback(null, true);

  if (acl.checkIfAclIsDisabledInController(req.options.controller, req._sails))
    return callback(null, true);

  var record;
  // get record from context - see sails-context
  if (req.context && req.context.record)
    record = req.context.record;

  var permissionName = acl.resolvePermissionName( req.options.controller + '_' + req.options.action );
  acl.can(permissionName, req.user, record, function (err, can) {
    if (err) return callback(err);
    if (!can) {
      if (!req.user) {
        req._sails.log.info('ACL:canPolicy: forbidden for unAuthenticated user:', permissionName);
      } else {
        req._sails.log.info('ACL:canPolicy: forbidden for user id: ', req.user.id, req.user.username, permissionName);
      }
    }

    return callback(null, can);
  });
}

/**
 * Check if acl is disabled to this controller
 *
 * @param  {string} controllerName
 * @param  {object} sails
 * @return {boolean} true for disabled or false for enabled
 */
acl.checkIfAclIsDisabledInController = function checkIfAclIsDisabledInController(controllerName, sails) {
  if (
    sails.controllers[controllerName] &&
    sails.controllers[controllerName]._config &&
    sails.controllers[controllerName]._config.acl === false
  ){
    // is disabled
    return true;
  }
  // not is disabled
  return false;
}

/**
 * Load all permissions from database
 *
 * @param  {object} sails
 * @param  {Function} callback
 * @return {object} waterline find promisse
 */
acl.loadAllPermissions = function loadAllPermissionsFromDB(sails, cb) {
  return sails.models.permission.find()
  .limit( 400 )
  .populate('roles')
  .exec(function found(err, matchingRecords) {
    if (err) return cb(err);
    var permissions = {};
    for (var i = matchingRecords.length - 1; i >= 0; i--) {
      permissions[ matchingRecords[i].name ] = matchingRecords[i];
      // lower case caches
      acl.lowerCasePermissionNames[matchingRecords[i].name.toLowerCase()] = matchingRecords[i].name;
    }
    return cb(null, permissions);
  });
}

/**
 * Load all roles from DB
 * @param  {object} sails
 * @param  {Function} cb  callback
 * @return {object} waterline find promisse
 */
acl.loadAllRoles = function loadAllRolesFromDB(sails, cb) {
  return sails.models.role.find()
  .limit( 50 )
  .exec(function found(err, matchingRecords) {
    if (err) return cb(err);
    var roles = {};
    for (var i = matchingRecords.length - 1; i >= 0; i--) {
      roles[matchingRecords[i].name] = matchingRecords[i];
    }
    return cb(null, roles);
  });
}

/**
 * Get all acl permissions from sails.controllers
 * @param  {object} sails
 * @param  {Function} callback
 */
acl.getAllActionPermisons = function(sails, callback) {
  var controllers = sails.controllers;
  var permissions = {};

  var controllerNames = Object.keys(sails.controllers);

  async.each(controllerNames, function (controllerName, next) {
    // skip if disabled
    if (acl.checkIfAclIsDisabledInController(controllerName, sails))
      return next();

    if (acl.hasBlueprint(controllerName, sails)) {
      permissions[controllerName + '_' + 'find'] =
        acl.generatePermissionObject(controllerName, 'find');

      permissions[controllerName + '_' + 'findOne'] =
        acl.generatePermissionObject(controllerName, 'findOne');

      permissions[controllerName + '_' + 'create'] =
        acl.generatePermissionObject(controllerName, 'create');

      permissions[controllerName + '_' + 'update'] =
        acl.generatePermissionObject(controllerName, 'update');

      permissions[controllerName + '_' + 'add'] =
        acl.generatePermissionObject(controllerName, 'add');

      permissions[controllerName + '_' + 'remove'] =
        acl.generatePermissionObject(controllerName, 'remove');

      permissions[controllerName + '_' + 'delete'] =
        acl.generatePermissionObject(controllerName, 'delete');
    }

    var actionNames = Object.keys(controllers[controllerName]);
    for (var i = actionNames.length - 1; i >= 0; i--) {
      if( !_.isObject(controllers[controllerName][actionNames[i]]) )
        continue;

      permissions[controllerName + '_' + actionNames[i]] =
        acl.generatePermissionObject(controllerName, actionNames[i]);
    }

    next();
  }, function(err) {
    callback(err, permissions);
  })
}

acl.generatePermissionObject = function (controllerName, actionName) {
  return {
    controller: controllerName,
    action: actionName,
    name: controllerName + '_' + actionName
  }
}

acl.hasBlueprint = function(name, sails) {

  // check if rest blueprint is disabled
  if (sails.controllers[name] && sails.controllers[name]._config) {
    if(sails.controllers[name]._config === false) {
      return false;
    }
  }
  // check if has a model with same name as this controller
  if( !sails.models[name])
    return false;

  return true;
}

/**
 * Check and create default roles required for all we.js projects
 *
 * @param  {object} sails
 * @param  {Function} cb callback
 */
acl.createDefaultRolesIfNotExists = function createDefaultRolesIfNotExists(sails, cb) {
  async.parallel([
    function unAuthenticatedRole(done) {
      acl.registerOneDefaltRole(sails, 'unAuthenticated', done);
    },
    function authenticatedRole(done) {
      acl.registerOneDefaltRole(sails, 'authenticated', done);
    },
    function ownerRole(done) {
      acl.registerOneDefaltRole(sails, 'owner', done);
    },
    function administratorRole(done) {
      acl.registerOneDefaltRole(sails, 'administrator', done);
    }
  ], cb)
}

/**
 * Create and register one of the default roles if not exists
 *
 * @param  {object} sails
 * @param  {string} roleName
 * @param  {Function} done / callback
 *
 */
acl.registerOneDefaltRole = function registerOneDefaltRole(sails, roleName, done) {
  // skip if this default role already exists
  if ( acl.roles[roleName] )
    return done();
  acl.createRole(sails, {  name: roleName }, done);
}

/**
 * Create and register one role
 *
 * @param {object} sails.js object
 * @param  {object} data { name: 'roleName', ... } the new role object
 * @param  {Function} cb  callback
 * @return {[type]} waterline create query promisse
 */
acl.createRole = function createRole(sails, data, cb) {
  // if not exists create the role
  return sails.models.role.create(data)
  .exec(function (err, role) {
    if (err) return cb(err);
    // set the role in global roles how will be avaible in sails.acl.roles
    acl.roles[data.name] = role;
    cb();
  })
}

module.exports = acl;<|endoftext|>


<commit_before>'use strict';
var
  _ = require('lodash'),
  fs = require('fs'),
  path = require('path'),
  yaml = require('js-yaml'),
  url = require('url'),
  util = require('util'),
  Endpoint;

/**
 * @module api
 * @desc Provides public facade layer around API endpoints.
 * @private
 */

/** @constant
    @type {string}
    @default
*/
var API_DEFINITIONS = path.normalize(path.join(__dirname, '..', 'config', 'apis.yml'));
/** @constant
    @type {string}
    @default
*/
var API_SPECS = path.normalize(path.join(__dirname, '..', 'config', 'specs.yml'));
/** @constant
    @type {string}
    @default
*/
var API_PREFIX = 'v1';

function getSSLOptions(config) {
  var options = {};

  if (config.has('ssl_cert_file')) {
    options.cert = fs.readFileSync(config.get('ssl_cert_file'));
  }

  if (config.has('ssl_pem_file')) {
    options.key = fs.readFileSync(config.get('ssl_pem_file'));
  }

  if (config.has('ssl_pem_passphrase')) {
    options.passphrase = config.get('ssl_pem_passphrase');
  }

  if (config.has('ssl_ca_cert')) {
    options.ca = fs.readFileSync(config.get('ssl_ca_cert'));
  }

  options.ciphers = config.get('ssl_ciphers');
  options.rejectUnauthorized = config.get('ssl_verify');

  return options;
}

function getProxy(config) {
  var proxy = {};

  if (config.has('proxy_address') && config.has('proxy_port')) {
    proxy.hostname = config.get('proxy_address');
    proxy.port = config.get('proxy_port');
  }

  if (config.has('proxy_username') && config.has('proxy_password')) {
    proxy.auth = config.get('proxy_username') + ':' + config.get('proxy_password');
  }

  if (!_.isEmpty(proxy)) {
    proxy.protocol = config.get('vault_ssl') === true ? 'https' : 'http';
    return {
      proxy: url.format(proxy)
    };
  }

  return proxy;
}

function getVaultURL(config) {
  return url.format({
    hostname: config.get('vault_host'),
    port: config.get('vault_port'),
    protocol: config.get('vault_ssl') === true ? 'https' : 'http'
  });
}

function load(config, metadata) {
  var
    defaults = {},
    base_url = getVaultURL(config) + '/' + API_PREFIX,
    partial;

  if (config.has('timeout')) {
    defaults.timeout = config.get('timeout');
  }

  defaults = _.assign(defaults, getSSLOptions(config));
  defaults = _.assign(defaults, getProxy(config));

  partial = _.partial(Endpoint.create, base_url, defaults);
  return _.transform(metadata, partial, {});
}

function loadAPISpecs(thisAPI, config, type, namespace) {
  namespace = namespace || type;
  var spec;

  if (_.isEmpty(thisAPI.specs)) {
    // could throw an error but properly installed and tested means custom
    // error handler could hide root issue
    thisAPI.specs = yaml.safeLoad(fs.readFileSync(API_SPECS, 'utf8'));
  }

  spec = _.mapKeys(thisAPI.specs[API_PREFIX][type], function (value, key) {
    return util.format(key, namespace);
  });

  return load(config, spec);
}

function loadAPIDefinitions(config) {
  var parsed_api;

  // could throw an error but properly installed and tested means custom
  // error handler could hide root issue
  parsed_api = yaml.safeLoad(fs.readFileSync(API_DEFINITIONS, 'utf8'));

  return load(config, parsed_api[API_PREFIX]);
}

/**
 * @private
 * @constructor API
 *
 * @param {Object} config overrides any default configurations.
 */
function API(config) {
  if (_.isUndefined(config) || _.isUndefined(config.get)) {
    throw new Error('API must be configurations object');
  }
  Endpoint = require('./endpoint')(config);

  /** @member {Object} */
  this.endpoints = {};
  /** @member {Object} */
  this.specs = {};

  _.extend(this.endpoints, loadAPIDefinitions(config));
  // default auth backend
  _.extend(this.endpoints, loadAPISpecs(this, config, 'token'));
  // default secret backend
  _.extend(this.endpoints, loadAPISpecs(this, config, 'secret'));
}

/**
 * @method mountEndpoints
 * @desc Mounts a set of Endpoints from a given spec type to a specified namespace.
 *
 * @param {Object} config overrides any default configurations.
 * @param {String} type the name of the spec that references a set of Endpoints.
 * @param {String} namespace the name to mount a specified set of Endpoints to.
 * @throws {Error} namespace not provided or has no length.
 * @throws {Error} type not provided or has no length.
 * @throws {Error} Could not find type in defintions.
 */
API.prototype.mountEndpoints = function mountEndpoints(config, type, namespace) {
  if (_.isUndefined(config) || _.isUndefined(config.get)) {
    throw new Error('configuration object not provided.');
  }
  if (!_.isString(type) || type.length === 0) {
    throw new Error('type not provided or has no length.');
  }
  if (!_.isString(namespace) || namespace.length === 0) {
    throw new Error('namespace not provided or has no length.');
  }
  _.extend(this.endpoints, loadAPISpecs(this, config, type, namespace));
};

/**
 * @method unmountEndpoints
 * @desc Unmounts a set of Endpoints from a specified namespace.
 *
 * @param {String} namespace the name associated with Endpoints to unmount .
 * @throws {Error} namespace not provided or has no length.
 * @throws {Error} Could not find namespace in APIs.
 */
API.prototype.unmountEndpoints = function unmountEndpoints(namespace) {
  if (!_.isString(namespace) || namespace.length === 0) {
    throw new Error('namespace not provided or has no length.');
  }
  var self = this;
  _.filter(_.keys(this.endpoints), function (key) {
    if (_.includes(key, namespace)) {
      delete self.endpoints[key];
    }
  });
};

/**
 * @method getEndpoint
 * @desc Retrieves the named endpoint.
 *
 * @param {String} endpoint the name / path of the defined endpoint.
 * @return {EndPoint} An instance matching the specificed name.
 * @throws {Error} Endpoint not provided or has no length.
 * @throws {Error} Could not find endpoint: {endpoint_name} in API defintions
 */
API.prototype.getEndpoint = function getEndpoint(endpoint_name) {
  if (!_.isString(endpoint_name) || endpoint_name.length === 0) {
    throw new Error('Endpoint not provided or has no length.');
  }
  var endpoint = _.get(this.endpoints, endpoint_name);

  if (_.isUndefined(endpoint)) {
    throw new Error('Could not find endpoint: ' + endpoint_name + ' in API defintions');
  }
  return endpoint;
};

module.exports = API;
<commit_msg>Fix a bug where mount names that are suffixed with a slash don't have their specs loaded correctly.<commit_after>'use strict';
var
  _ = require('lodash'),
  fs = require('fs'),
  path = require('path'),
  yaml = require('js-yaml'),
  url = require('url'),
  util = require('util'),
  Endpoint;

/**
 * @module api
 * @desc Provides public facade layer around API endpoints.
 * @private
 */

/** @constant
    @type {string}
    @default
*/
var API_DEFINITIONS = path.normalize(path.join(__dirname, '..', 'config', 'apis.yml'));
/** @constant
    @type {string}
    @default
*/
var API_SPECS = path.normalize(path.join(__dirname, '..', 'config', 'specs.yml'));
/** @constant
    @type {string}
    @default
*/
var API_PREFIX = 'v1';

function getSSLOptions(config) {
  var options = {};

  if (config.has('ssl_cert_file')) {
    options.cert = fs.readFileSync(config.get('ssl_cert_file'));
  }

  if (config.has('ssl_pem_file')) {
    options.key = fs.readFileSync(config.get('ssl_pem_file'));
  }

  if (config.has('ssl_pem_passphrase')) {
    options.passphrase = config.get('ssl_pem_passphrase');
  }

  if (config.has('ssl_ca_cert')) {
    options.ca = fs.readFileSync(config.get('ssl_ca_cert'));
  }

  options.ciphers = config.get('ssl_ciphers');
  options.rejectUnauthorized = config.get('ssl_verify');

  return options;
}

function getProxy(config) {
  var proxy = {};

  if (config.has('proxy_address') && config.has('proxy_port')) {
    proxy.hostname = config.get('proxy_address');
    proxy.port = config.get('proxy_port');
  }

  if (config.has('proxy_username') && config.has('proxy_password')) {
    proxy.auth = config.get('proxy_username') + ':' + config.get('proxy_password');
  }

  if (!_.isEmpty(proxy)) {
    proxy.protocol = config.get('vault_ssl') === true ? 'https' : 'http';
    return {
      proxy: url.format(proxy)
    };
  }

  return proxy;
}

function getVaultURL(config) {
  return url.format({
    hostname: config.get('vault_host'),
    port: config.get('vault_port'),
    protocol: config.get('vault_ssl') === true ? 'https' : 'http'
  });
}

function load(config, metadata) {
  var
    defaults = {},
    base_url = getVaultURL(config) + '/' + API_PREFIX,
    partial;

  if (config.has('timeout')) {
    defaults.timeout = config.get('timeout');
  }

  defaults = _.assign(defaults, getSSLOptions(config));
  defaults = _.assign(defaults, getProxy(config));

  partial = _.partial(Endpoint.create, base_url, defaults);
  return _.transform(metadata, partial, {});
}

function loadAPISpecs(thisAPI, config, type, namespace) {
  namespace = namespace || type;
  var spec;

  if (_.isEmpty(thisAPI.specs)) {
    // could throw an error but properly installed and tested means custom
    // error handler could hide root issue
    thisAPI.specs = yaml.safeLoad(fs.readFileSync(API_SPECS, 'utf8'));
  }

  spec = _.mapKeys(thisAPI.specs[API_PREFIX][type], function (value, key) {
    return util.format(key, namespace);
  });

  return load(config, spec);
}

function loadAPIDefinitions(config) {
  var parsed_api;

  // could throw an error but properly installed and tested means custom
  // error handler could hide root issue
  parsed_api = yaml.safeLoad(fs.readFileSync(API_DEFINITIONS, 'utf8'));

  return load(config, parsed_api[API_PREFIX]);
}

/**
 * @private
 * @constructor API
 *
 * @param {Object} config overrides any default configurations.
 */
function API(config) {
  if (_.isUndefined(config) || _.isUndefined(config.get)) {
    throw new Error('API must be configurations object');
  }
  Endpoint = require('./endpoint')(config);

  /** @member {Object} */
  this.endpoints = {};
  /** @member {Object} */
  this.specs = {};

  _.extend(this.endpoints, loadAPIDefinitions(config));
  // default auth backend
  _.extend(this.endpoints, loadAPISpecs(this, config, 'token'));
  // default secret backend
  _.extend(this.endpoints, loadAPISpecs(this, config, 'secret'));
}

/**
 * @method mountEndpoints
 * @desc Mounts a set of Endpoints from a given spec type to a specified namespace.
 *
 * @param {Object} config overrides any default configurations.
 * @param {String} type the name of the spec that references a set of Endpoints.
 * @param {String} namespace the name to mount a specified set of Endpoints to.
 * @throws {Error} namespace not provided or has no length.
 * @throws {Error} type not provided or has no length.
 * @throws {Error} Could not find type in defintions.
 */
API.prototype.mountEndpoints = function mountEndpoints(config, type, namespace) {
  if (_.isUndefined(config) || _.isUndefined(config.get)) {
    throw new Error('configuration object not provided.');
  }
  if (!_.isString(type) || type.length === 0) {
    throw new Error('type not provided or has no length.');
  }
  if (!_.isString(namespace) || namespace.length === 0) {
    throw new Error('namespace not provided or has no length.');
  }
  if (_.endsWith(namespace, '/')) {
    namespace = namespace.slice(0, -1);
  }

  _.extend(this.endpoints, loadAPISpecs(this, config, type, namespace));
};

/**
 * @method unmountEndpoints
 * @desc Unmounts a set of Endpoints from a specified namespace.
 *
 * @param {String} namespace the name associated with Endpoints to unmount .
 * @throws {Error} namespace not provided or has no length.
 * @throws {Error} Could not find namespace in APIs.
 */
API.prototype.unmountEndpoints = function unmountEndpoints(namespace) {
  if (!_.isString(namespace) || namespace.length === 0) {
    throw new Error('namespace not provided or has no length.');
  }
  var self = this;
  _.filter(_.keys(this.endpoints), function (key) {
    if (_.includes(key, namespace)) {
      delete self.endpoints[key];
    }
  });
};

/**
 * @method getEndpoint
 * @desc Retrieves the named endpoint.
 *
 * @param {String} endpoint the name / path of the defined endpoint.
 * @return {EndPoint} An instance matching the specificed name.
 * @throws {Error} Endpoint not provided or has no length.
 * @throws {Error} Could not find endpoint: {endpoint_name} in API defintions
 */
API.prototype.getEndpoint = function getEndpoint(endpoint_name) {
  if (!_.isString(endpoint_name) || endpoint_name.length === 0) {
    throw new Error('Endpoint not provided or has no length.');
  }
  var endpoint = _.get(this.endpoints, endpoint_name);

  if (_.isUndefined(endpoint)) {
    throw new Error('Could not find endpoint: ' + endpoint_name + ' in API defintions');
  }
  return endpoint;
};

module.exports = API;
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var request = require('request');
var replacestream = require('replacestream');
var zlib = require('zlib');
var changes = require('concurrent-couch-follower');

var config = require('./configLoader');

var logger = config.logger;

var api = {};

var scopedModuleRegexp = new RegExp('^\\/\\@([^/]+)%2f.+$');
var httpCentralUrl = config.centralURL.replace('https', 'http');
var httpsCentralUrl = httpCentralUrl.replace('http', 'https');
var sequenceFile = path.join(config.attachmentPath, '.sequence');

/**
 * Serve attachment from file system if present, else serve it from central registry (and save it on file system)
 * @param req request
 * @param resp response
 */
api.manageAttachment = function(req, resp) {
  var filePath = path.join(config.attachmentPath, req.url);
  fs.exists(filePath, function(exists) {
    if (exists) {
      logger.debug('%s: serving attachment from file system: %s', req.url, filePath);
      fs.createReadStream(filePath).pipe(resp);
    } else {
      var downloadURL = config.centralURL + req.url;
      logger.debug('%s: downloading, saving and serving attachment from central registry: %s', req.url, downloadURL);
      var couchDBStream = request(downloadURL);
      req.pipe(couchDBStream);
      couchDBStream.pipe(resp);
      mkdirp(path.dirname(filePath), function() {
        couchDBStream.pipe(fs.createWriteStream(filePath));
      });
    }
  });
};

function proxyToCentralRepositoryAndReplaceUrls(req, resp) {
  var couchDBUrl = config.centralURL;

  logger.debug('%s: forwarding to repository (%s) and responding with attachment URL replacement', req.url, couchDBUrl);

  var couchDBStream = request(couchDBUrl + req.url);
  req.pipe(couchDBStream);
  couchDBStream.on('response', function(response) {
    var contentEncoding = response.headers['content-encoding'];
    if (contentEncoding === 'gzip') {
      // Response is gzip-ed, we ungzip it in order to be able to replace content
      response = response.pipe(zlib.createGunzip());
    }
    response
      .pipe(replacestream(httpsCentralUrl, config.proxyURL))
      .pipe(replacestream(httpCentralUrl, config.proxyURL))
      .pipe(resp);
  });
}

/**
 * Serve JSON of a module from local (or central) registry with attachment URL replacement
 * @param req request
 * @param resp response
 */
api.manageModule = function(req, resp) {
  // FIXME: For now, scoped modules are not synced via couchdb (https://github.com/npm/registry/issues/13), so we query the central one instead
  var isScopedModule = req.url.match(scopedModuleRegexp) != null;

  if (isScopedModule) {
    proxyToCentralRepositoryAndReplaceUrls(req, resp);
  } else {
    var filePath = path.join(config.attachmentPath, req.url + '.json');
    fs.exists(filePath, function(exists) {
      if (exists) {
        logger.debug('%s: serving JSON from file system: %s', req.url, filePath);
        fs.createReadStream(filePath)
          .pipe(replacestream(httpsCentralUrl, config.proxyURL))
          .pipe(replacestream(httpCentralUrl, config.proxyURL))
          .pipe(resp);
      } else {
        var downloadURL = config.centralURL + req.url;
        logger.debug('%s: downloading, saving and serving JSON from central registry: %s', req.url, downloadURL);
        mkdirp(path.dirname(filePath), function() {
          var couchDBStream = request(downloadURL);
          req.pipe(couchDBStream);
          couchDBStream.on('response', function(response) {
            var contentEncoding = response.headers['content-encoding'];
            if (contentEncoding === 'gzip') {
              // Response is gzip-ed, we ungzip it in order to be able to replace content
              response = response.pipe(zlib.createGunzip());
            }
            response.pipe(fs.createWriteStream(filePath));
            response
              .pipe(replacestream(httpsCentralUrl, config.proxyURL))
              .pipe(replacestream(httpCentralUrl, config.proxyURL))
              .pipe(resp);
          });
        });
      }
    });
  }
};

/**
 * Proxy to central repository
 * @param req request
 * @param resp response
 */
api.proxyToCentralRepository = function(req, resp) {
  var centralCouchDBUrl = config.centralURL;
  logger.debug('%s: forwarding to central repository (%s)', req.url, centralCouchDBUrl);
  var centralCouchDBStream = request(centralCouchDBUrl + req.url);
  req.pipe(centralCouchDBStream);
  centralCouchDBStream.pipe(resp);
};

api.initChangesSync = function() {
  mkdirp.sync(path.dirname(sequenceFile));
  var sequenceNumberToRestartFrom;
  if (fs.existsSync(sequenceFile)) {
    sequenceNumberToRestartFrom = parseInt(fs.readFileSync(sequenceFile, 'utf-8'));
  }
  sequenceNumberToRestartFrom = sequenceNumberToRestartFrom || 'now';

  if (sequenceNumberToRestartFrom === 'now') {
    logger.debug('Starting sync from latest sequence number on %s', config.skimdbURL);
  } else {
    logger.debug('Restarting sync from %s since sequence number %s', config.skimdbURL, sequenceNumberToRestartFrom);
  }

  changes(function(data, done) {
    var filePath = path.join(config.attachmentPath, data.id + '.json');

    fs.exists(filePath, function(exists) {
      if (exists) {
        logger.debug('Updating %s module (seq: %s)', data.id, data.seq);
        fs.writeFile(filePath, JSON.stringify(data), done);
      } else {
        logger.trace('New %s module (seq: %s), but we don\'t use it', data.id, data.seq);
        done();
      }
    });
  }, {
    db: config.skimdbURL,
    since: sequenceNumberToRestartFrom,
    sequence: sequenceFile,
    include_docs: true,
    concurrency: 1
  });
};

module.exports = api;
<commit_msg>BUGFIX: write "doc" attribute as JSON instead of full JSON<commit_after>var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var request = require('request');
var replacestream = require('replacestream');
var zlib = require('zlib');
var changes = require('concurrent-couch-follower');

var config = require('./configLoader');

var logger = config.logger;

var api = {};

var scopedModuleRegexp = new RegExp('^\\/\\@([^/]+)%2f.+$');
var httpCentralUrl = config.centralURL.replace('https', 'http');
var httpsCentralUrl = httpCentralUrl.replace('http', 'https');
var sequenceFile = path.join(config.attachmentPath, '.sequence');

/**
 * Serve attachment from file system if present, else serve it from central registry (and save it on file system)
 * @param req request
 * @param resp response
 */
api.manageAttachment = function(req, resp) {
  var filePath = path.join(config.attachmentPath, req.url);
  fs.exists(filePath, function(exists) {
    if (exists) {
      logger.debug('%s: serving attachment from file system: %s', req.url, filePath);
      fs.createReadStream(filePath).pipe(resp);
    } else {
      var downloadURL = config.centralURL + req.url;
      logger.debug('%s: downloading, saving and serving attachment from central registry: %s', req.url, downloadURL);
      var couchDBStream = request(downloadURL);
      req.pipe(couchDBStream);
      couchDBStream.pipe(resp);
      mkdirp(path.dirname(filePath), function() {
        couchDBStream.pipe(fs.createWriteStream(filePath));
      });
    }
  });
};

function proxyToCentralRepositoryAndReplaceUrls(req, resp) {
  var couchDBUrl = config.centralURL;

  logger.debug('%s: forwarding to repository (%s) and responding with attachment URL replacement', req.url, couchDBUrl);

  var couchDBStream = request(couchDBUrl + req.url);
  req.pipe(couchDBStream);
  couchDBStream.on('response', function(response) {
    var contentEncoding = response.headers['content-encoding'];
    if (contentEncoding === 'gzip') {
      // Response is gzip-ed, we ungzip it in order to be able to replace content
      response = response.pipe(zlib.createGunzip());
    }
    response
      .pipe(replacestream(httpsCentralUrl, config.proxyURL))
      .pipe(replacestream(httpCentralUrl, config.proxyURL))
      .pipe(resp);
  });
}

/**
 * Serve JSON of a module from local (or central) registry with attachment URL replacement
 * @param req request
 * @param resp response
 */
api.manageModule = function(req, resp) {
  // FIXME: For now, scoped modules are not synced via couchdb (https://github.com/npm/registry/issues/13), so we query the central one instead
  var isScopedModule = req.url.match(scopedModuleRegexp) != null;

  if (isScopedModule) {
    proxyToCentralRepositoryAndReplaceUrls(req, resp);
  } else {
    var filePath = path.join(config.attachmentPath, req.url + '.json');
    fs.exists(filePath, function(exists) {
      if (exists) {
        logger.debug('%s: serving JSON from file system: %s', req.url, filePath);
        fs.createReadStream(filePath)
          .pipe(replacestream(httpsCentralUrl, config.proxyURL))
          .pipe(replacestream(httpCentralUrl, config.proxyURL))
          .pipe(resp);
      } else {
        var downloadURL = config.centralURL + req.url;
        logger.debug('%s: downloading, saving and serving JSON from central registry: %s', req.url, downloadURL);
        mkdirp(path.dirname(filePath), function() {
          var couchDBStream = request(downloadURL);
          req.pipe(couchDBStream);
          couchDBStream.on('response', function(response) {
            var contentEncoding = response.headers['content-encoding'];
            if (contentEncoding === 'gzip') {
              // Response is gzip-ed, we ungzip it in order to be able to replace content
              response = response.pipe(zlib.createGunzip());
            }
            response.pipe(fs.createWriteStream(filePath));
            response
              .pipe(replacestream(httpsCentralUrl, config.proxyURL))
              .pipe(replacestream(httpCentralUrl, config.proxyURL))
              .pipe(resp);
          });
        });
      }
    });
  }
};

/**
 * Proxy to central repository
 * @param req request
 * @param resp response
 */
api.proxyToCentralRepository = function(req, resp) {
  var centralCouchDBUrl = config.centralURL;
  logger.debug('%s: forwarding to central repository (%s)', req.url, centralCouchDBUrl);
  var centralCouchDBStream = request(centralCouchDBUrl + req.url);
  req.pipe(centralCouchDBStream);
  centralCouchDBStream.pipe(resp);
};

api.initChangesSync = function() {
  mkdirp.sync(path.dirname(sequenceFile));
  var sequenceNumberToRestartFrom;
  if (fs.existsSync(sequenceFile)) {
    sequenceNumberToRestartFrom = parseInt(fs.readFileSync(sequenceFile, 'utf-8'));
  }
  sequenceNumberToRestartFrom = sequenceNumberToRestartFrom || 'now';

  if (sequenceNumberToRestartFrom === 'now') {
    logger.debug('Starting sync from latest sequence number on %s', config.skimdbURL);
  } else {
    logger.debug('Restarting sync from %s since sequence number %s', config.skimdbURL, sequenceNumberToRestartFrom);
  }

  changes(function(data, done) {
    var filePath = path.join(config.attachmentPath, data.id + '.json');

    fs.exists(filePath, function(exists) {
      if (exists) {
        logger.debug('Updating %s module (seq: %s)', data.id, data.seq);
        fs.writeFile(filePath, JSON.stringify(data.doc), done);
      } else {
        logger.trace('New %s module (seq: %s), but we don\'t use it', data.id, data.seq);
        done();
      }
    });
  }, {
    db: config.skimdbURL,
    since: sequenceNumberToRestartFrom,
    sequence: sequenceFile,
    include_docs: true,
    concurrency: 1
  });
};

module.exports = api;
<|endoftext|>