<commit_before>import some from 'lodash/some';

import apiRequest from '../apiBase/apiRequest';
import BadCaptchaError from '../apiBase/errors/BadCaptchaError';
import ValidationError from '../apiBase/errors/ValidationError';
import PostModel from '../models2/PostModel';
import { formatBaseContentQuery } from './BaseContentEndpoint';

const BAD_CAPTCHA = 'BAD_CAPTCHA';

const getPath = (query) => {
  if (query.user) {
    return `user/${query.user}/submitted.json`;
  } else if (query.id) {
    return `by_id/${query.id}.json`;
  } else if (query.ids) {
    return `by_id/${query.query.ids.join(',')}.json`;
  } else if (query.subredditName) {
    if (query.sort) {
      return `r/${query.subredditName}/${query.sort}.json`;
    }
    return `r/${query.subredditName}.json`;
  } else if (query.multi) {
    return `user/${query.multiUser}/m/${query.multi}.json`;
  }

  return `${query.sort || 'hot'}.json`;
};

const formatQuery = (query, method) => {
  formatBaseContentQuery(query, method);

  if (method !== 'patch') {
    query.feature = 'link_preview';
    query.sr_detail = 'true';
  }

  if (method === 'del') {
    query._method = 'post';
  }

  return query;
};

const formatPostData = (data)=> {
  const postData = {
    api_type: 'json',
    thing_id: data.thingId,
    title: data.title,
    kind: data.kind,
    sendreplies: data.sendreplies,
    sr: data.sr,
    iden: data.iden,
    captcha: data.captcha,
    resubmit: data.resubmit,
  };

  if (data.text) {
    postData.text = data.text;
  } else if (data.url) {
    postData.url = data.url;
  }

  return postData;
};

const handleGet = apiResponse => {
  const { body: { data } } = apiResponse.response;

  if (data && data.children && data.children[0]) {
    if (data.children.length === 1) {
      apiResponse.addResult(PostModel.fromJSON(data.children[0].data));
    } else {
      data.children.forEach(c => apiResponse.addResult(PostModel.fromJSON(c.data)));
    }
  }

  return apiResponse;
};

export default {
  get(apiOptions, _query) {
    const path = getPath(_query);
    const query = formatQuery({ raw_json: 1, ..._query }, 'get');

    return apiRequest(apiOptions, 'GET', path, { query }).then(handleGet);
  },

  post(apiOptions, data) {
    const path = 'api/submit';
    const query = formatPostData(data);

    return apiRequest(apiOptions, 'POST', path, { query })
      .then(apiResponse => {
        const { body: { json } } = apiResponse.response;
        if (json.errors.length && some(json.errors, e => e[0] === BAD_CAPTCHA)) {
          throw new BadCaptchaError(data.captcha, json.captcha, json.errors);
        } else if (json.errors.length) {
          throw new ValidationError(path, json.errors, 200);
        } else {
          return apiResponse.response.body;
        }
      });
  },
};
<commit_msg>Correctly create the multi id url.<commit_after>import some from 'lodash/some';

import apiRequest from '../apiBase/apiRequest';
import BadCaptchaError from '../apiBase/errors/BadCaptchaError';
import ValidationError from '../apiBase/errors/ValidationError';
import PostModel from '../models2/PostModel';
import { formatBaseContentQuery } from './BaseContentEndpoint';

const BAD_CAPTCHA = 'BAD_CAPTCHA';

const getPath = (query) => {
  if (query.user) {
    return `user/${query.user}/submitted.json`;
  } else if (query.id) {
    return `by_id/${query.id}.json`;
  } else if (query.ids) {
    return `by_id/${query.ids.join(',')}.json`;
  } else if (query.subredditName) {
    if (query.sort) {
      return `r/${query.subredditName}/${query.sort}.json`;
    }
    return `r/${query.subredditName}.json`;
  } else if (query.multi) {
    return `user/${query.multiUser}/m/${query.multi}.json`;
  }

  return `${query.sort || 'hot'}.json`;
};

const formatQuery = (query, method) => {
  formatBaseContentQuery(query, method);

  if (method !== 'patch') {
    query.feature = 'link_preview';
    query.sr_detail = 'true';
  }

  if (method === 'del') {
    query._method = 'post';
  }

  return query;
};

const formatPostData = (data)=> {
  const postData = {
    api_type: 'json',
    thing_id: data.thingId,
    title: data.title,
    kind: data.kind,
    sendreplies: data.sendreplies,
    sr: data.sr,
    iden: data.iden,
    captcha: data.captcha,
    resubmit: data.resubmit,
  };

  if (data.text) {
    postData.text = data.text;
  } else if (data.url) {
    postData.url = data.url;
  }

  return postData;
};

const handleGet = apiResponse => {
  const { body: { data } } = apiResponse.response;

  if (data && data.children && data.children[0]) {
    if (data.children.length === 1) {
      apiResponse.addResult(PostModel.fromJSON(data.children[0].data));
    } else {
      data.children.forEach(c => apiResponse.addResult(PostModel.fromJSON(c.data)));
    }
  }

  return apiResponse;
};

export default {
  get(apiOptions, _query) {
    const path = getPath(_query);
    const query = formatQuery({ raw_json: 1, ..._query }, 'get');

    return apiRequest(apiOptions, 'GET', path, { query }).then(handleGet);
  },

  post(apiOptions, data) {
    const path = 'api/submit';
    const query = formatPostData(data);

    return apiRequest(apiOptions, 'POST', path, { query })
      .then(apiResponse => {
        const { body: { json } } = apiResponse.response;
        if (json.errors.length && some(json.errors, e => e[0] === BAD_CAPTCHA)) {
          throw new BadCaptchaError(data.captcha, json.captcha, json.errors);
        } else if (json.errors.length) {
          throw new ValidationError(path, json.errors, 200);
        } else {
          return apiResponse.response.body;
        }
      });
  },
};
<|endoftext|>


<commit_before>import jwt from 'jsonwebtoken';

function checkAuth(router) {
  router.use((req, res, next) => {
    if (/^\/v1\/auth\/(login|register)$/i.test(req.path)) return next();

    if (!req.query.token) {
      return res.status(422).json({
        'error': 'no token set'
      });
    }

    // TODO: should token be static in config or generated every x time units?
    jwt.verify(req.query.body, 'testSecret', (err, decoded) => {
      // TODO: check if token is valid and either send back 200 with the required data
      // or 403
    });
  });
}

function checkPermission(router) {
  router.use((req, res, next) => {
    // TODO: add permission handling
  });
}

function rateLimiter(router) {
  router.use((req, res, next) => {
    // TODO: add rate limiting
  });
}

export default function middleware(router, config) {
  if (!router || !config) return;

  if (config.rateLimiter) {
    rateLimiter(router);
  }

  if (config.checkAuthentication) {
    checkAuth(router);
  }

  if (config.checkPermission) {
    checkPermission(router);
  }
}
<commit_msg>remove old middleware collection<commit_after><|endoftext|>


<commit_before>import Params from "./params";

const Registry = require("./registry");
var FeatureRenderer2D = require("../view/render2D/featureRenderer2D");
import Parameter from './parameter';

/**
 * This class contains the component abstraction used in the interchange format and the
 * high level device model of the microfluidic.
 */
class Component {

    /**
     * Default Constructor
     * @param type
     * @param params
     * @param name
     * @param mint
     * @param id
     */
    constructor(type, params, name, mint, id = Component.generateID()){
        this.__params = params;
        this.__name = name;
        this.__id = id;
        this.__type = type;
        this.__entity = mint;
        //This stores the features that are a part of the component
        this.__features = [];
        //TODO: Need to figure out how to effectively search through these
        this.__bounds = null;
    }

    /**
     * Returns an array of strings that are the feature ids of the component
     * @return {Array}
     */
    get features(){
        return this.__features;
    }

    /**
     * Generates a random id
     * @returns {String} Random ID string
     */
    static generateID() {
        return Registry.generateID();
    }

    /**
     * Sets the bounds i.e. the x,y position and the width and length of the component
     * @param bounds PaperJS Rectangle object associated with a Path.bounds property
     */
    setBounds(bounds){
        this.__bounds = bounds;
        let topleftpt = bounds.topLeft;
        this.__params.position = [topleftpt.x, topleftpt.y];
        this.__params.xspan = bounds.width;
        this.__params.yspan = bounds.height;
    }

    /**
     * Updates the parameters stored by the component
     * @param key
     * @param value
     */
    updateParameter(key, value){
        this.__params[key] = value;
    }

    /**
     * Generates the object that needs to be serialzed into JSON for interchange format V1
     * @returns {{}} Object
     */
    toInterchangeV1(){
        let output = {};
        output.id = this.__id;
        output.name = this.__name;
        output.entity = this.__entity;
        output.params = this.__params.toJSON();
        let bounds = this.getBoundingRectangle()
        output.xspan = bounds.width;
        output.yspan = bounds.height;
        return output;
    }

    /**
     * Returns the ID of the component
     * @returns {String|*}
     */
    getID(){
        return this.__id;
    }

    /**
     * Allows the user to set the name of the component
     * @param name
     */
    setName(name){
        console.log("test", name);
        this.__name = name;
    }

    /**
     * Returns the name of the component
     * @returns {String}
     */
    getName(){
        return this.__name;
    }

    /**
     * Gets the 3DuF Type of the component, this will soon be depreciated and merged with
     * the MINT references
     * @returns {*}
     */
    getType(){
        return this.__type;
    }

    /**
     * Returns the position of the component
     * @return {*|string}
     */
    getPosition(){
        return this.__params.getValue("position");
    }

    /**
     * Returns the value of the parameter stored against the following key in teh component params
     * @param key
     * @returns {*}
     */
    getValue(key){
        try {
            return this.__params[key].getValue();
        } catch (err){
            if (this.hasDefaultParam(key)) return this.getDefaults()[key];
            else throw new Error("Unable to get value for key: " + key);
        }
    }

    /**
     * Returns the list of feature ids that are associated with this
     * component
     * @return {Array}
     */
    getFeatureIDs(){
        return this.__features;
    }

    /**
     * Not sure what this does
     * @param key
     * @returns {boolean}
     */
    hasDefaultParam(key){
        if (this.getDefaults().hasOwnProperty(key)) return true;
        else return false;
    }

    /**
     * Adds a feature that is associated with the component
     * @param featureID String id of the feature
     */
    addFeatureID(featureID){
        if(typeof featureID != 'string' && !(featureID instanceof String)){
            throw new Error("The reference object value can only be a string")
        }
        this.__features.push(featureID);
        //Now update bounds
        // this.__updateBounds();
    }

    /**
     * This method updates the bounds of the component
     * @private
     */
    __updateBounds() {
        console.log("test");
        let bounds = null;
        let feature = null;
        let renderedfeature = null;
        for(var i in this.__features){
            // gets teh feature defined by the id
            feature = Registry.currentDevice.getFeatureByID(this.__features[i]);
            console.log(feature);
            renderedfeature = FeatureRenderer2D.renderFeature(feature);
            console.log("rendered:");
            console.log(renderedfeature);
            if(bounds == null){
                bounds = renderedfeature.bounds;
            }else{
                bounds = bounds.unite(renderedfeature.bounds);
            }
        }
        this.__bounds = bounds;
    }

    /**
     * Returns the params associated with the component
     */
    getParams(){
        return this.__params;
    }

    /**
     * Sets the params associated with the component
     * @param params key -> Parameter Set
     */
    setParams(params){
        this.__params = params;
        //TODO: Modify all the associated Features
        for(let key in params){
            let value = params[key];
            for(let i in this.__features){
                let featureidtochange = this.__features[i];

                //Get the feature id and modify it
                let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
                feature.updateParameter(key, value.getValue());
            }

        }
    }

    /**
     * Returns a paper.Rectangle object that defines the bounds of the component
     * @return {*}
     */
    getBoundingRectangle(){
        if(this.features.length == 0 || this.features == null || this.features == undefined){
            console.error("No features associated with the component");
        }
        let bounds  = null;
        for(let i in this.features){
            let featureid = this.features[i];
            let render = Registry.viewManager.view.getRenderedFeature(featureid);
            if(bounds){
                bounds = bounds.unite(render.bounds);
            }else{
                bounds = render.bounds;
            }
        }

        return bounds;
    }

    /**
     * Updates the coordinates of the component and all the other features
     * @param center
     */
    updateComponetPosition(center){
        this.updateParameter('position', new Parameter('Point', center));
        for(let i in this.__features){
            let featureidtochange = this.__features[i];

            let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
            // feature.updateParameter('position', center);
            feature.updateParameter('position', center);
        }
    }

    /**
     * Replicates the component at the given positions
     * @param xpos Integer location of X
     * @param ypos Integer location of Y
     * @param name
     * @return {Component}
     */
    replicate(xpos, ypos, name = Registry.currentDevice.generateNewName(this.__type)){
        //TODO: Fix this ridiculous chain of converting params back and forth, there should be an easier way
        //Converting all the params into raw values
        let replicaparams = {};
        for(let key in this.__params){
            replicaparams[key] = this.getValue(key);
        }
        let ret = new Component(this.__type, replicaparams, name, this.__entity);
        console.log("Checking what the new component params are:", ret.__params);
        //Generate New features
        for(let i in this.features){
            let feature = Registry.currentDevice.getFeatureByID(this.features[i]);
            console.log("test", this.getPosition()[0], this.getPosition()[1] ,this.getPosition());
            let replica = feature.replicate(this.getPosition()[0], this.getPosition()[1]);
            ret.features.push(replica.getID());

            //TODO: add new feature to the layer in which the current feature is in
            let currentlayer = Registry.currentDevice.getLayerFromFeatureID(this.features[i]);
            currentlayer.addFeature(replica);

        }
        console.warn("TODO: Generate renders for the new Features for this new component");
        ret.updateComponetPosition([xpos, ypos]);
        return ret;
    }

    /**
     * This method is used to import the component from Interchange V1 JSON
     * @param json
     * @returns {*}
     */
    static fromInterchangeV1(json){
        // let set;
        // if (json.hasOwnProperty("set")) set = json.set;
        // else set = "Basic";
        // //TODO: This will have to change soon when the thing is updated
        // throw new Error("Need to implement Interchange V1 Import for component object");
        let name = json.name;
        let id = json.id;
        let entity = json.entity;
        let params = {};
        for(let key in json.params){
            console.log("key:", key, "value:", json.params[key]);
            let paramobject = Parameter.generateComponentParameter(key, json.params[key]);
            params[key] = paramobject;
        }

        let paramstoadd = new Params(null, null, null, params);

        let component = new Component(entity, paramstoadd, name, entity, id);
        return component;

    }

}

module.exports = Component;<commit_msg>Fix param replication issue that broke the generate array.<commit_after>import Params from "./params";

const Registry = require("./registry");
const FeatureRenderer2D = require("../view/render2D/featureRenderer2D");
import Parameter from './parameter';

/**
 * This class contains the component abstraction used in the interchange format and the
 * high level device model of the microfluidic.
 */
class Component {

    /**
     * Default Constructor
     * @param type
     * @param params
     * @param name
     * @param mint
     * @param id
     */
    constructor(type, params, name, mint, id = Component.generateID()){
        this.__params = params;
        this.__name = name;
        this.__id = id;
        this.__type = type;
        this.__entity = mint;
        //This stores the features that are a part of the component
        this.__features = [];
        //TODO: Need to figure out how to effectively search through these
        this.__bounds = null;
    }

    /**
     * Returns an array of strings that are the feature ids of the component
     * @return {Array}
     */
    get features(){
        return this.__features;
    }

    /**
     * Generates a random id
     * @returns {String} Random ID string
     */
    static generateID() {
        return Registry.generateID();
    }

    /**
     * Sets the bounds i.e. the x,y position and the width and length of the component
     * @param bounds PaperJS Rectangle object associated with a Path.bounds property
     */
    setBounds(bounds){
        this.__bounds = bounds;
        let topleftpt = bounds.topLeft;
        this.__params.position = [topleftpt.x, topleftpt.y];
        this.__params.xspan = bounds.width;
        this.__params.yspan = bounds.height;
    }

    /**
     * Updates the parameters stored by the component
     * @param key
     * @param value
     */
    updateParameter(key, value){
        this.__params[key] = value;
    }

    /**
     * Generates the object that needs to be serialzed into JSON for interchange format V1
     * @returns {{}} Object
     */
    toInterchangeV1(){
        let output = {};
        output.id = this.__id;
        output.name = this.__name;
        output.entity = this.__entity;
        output.params = this.__params.toJSON();
        let bounds = this.getBoundingRectangle()
        output.xspan = bounds.width;
        output.yspan = bounds.height;
        return output;
    }

    /**
     * Returns the ID of the component
     * @returns {String|*}
     */
    getID(){
        return this.__id;
    }

    /**
     * Allows the user to set the name of the component
     * @param name
     */
    setName(name){
        console.log("test", name);
        this.__name = name;
    }

    /**
     * Returns the name of the component
     * @returns {String}
     */
    getName(){
        return this.__name;
    }

    /**
     * Gets the 3DuF Type of the component, this will soon be depreciated and merged with
     * the MINT references
     * @returns {*}
     */
    getType(){
        return this.__type;
    }

    /**
     * Returns the position of the component
     * @return {*|string}
     */
    getPosition(){
        return this.__params.getValue("position");
    }

    /**
     * Returns the value of the parameter stored against the following key in teh component params
     * @param key
     * @returns {*}
     */
    getValue(key){
        try {
            return this.__params.getValue(key);
        } catch (err){
            throw new Error("Unable to get value for key: " + key);
        }
    }

    /**
     * Returns the list of feature ids that are associated with this
     * component
     * @return {Array}
     */
    getFeatureIDs(){
        return this.__features;
    }

    /**
     * Not sure what this does
     * @param key
     * @returns {boolean}
     */
    hasDefaultParam(key){
        if (this.getDefaults().hasOwnProperty(key)) return true;
        else return false;
    }

    /**
     * Adds a feature that is associated with the component
     * @param featureID String id of the feature
     */
    addFeatureID(featureID){
        if(typeof featureID != 'string' && !(featureID instanceof String)){
            throw new Error("The reference object value can only be a string")
        }
        this.__features.push(featureID);
        //Now update bounds
        // this.__updateBounds();
    }

    /**
     * This method updates the bounds of the component
     * @private
     */
    __updateBounds() {
        console.log("test");
        let bounds = null;
        let feature = null;
        let renderedfeature = null;
        for(var i in this.__features){
            // gets teh feature defined by the id
            feature = Registry.currentDevice.getFeatureByID(this.__features[i]);
            console.log(feature);
            renderedfeature = FeatureRenderer2D.renderFeature(feature);
            console.log("rendered:");
            console.log(renderedfeature);
            if(bounds == null){
                bounds = renderedfeature.bounds;
            }else{
                bounds = bounds.unite(renderedfeature.bounds);
            }
        }
        this.__bounds = bounds;
    }

    /**
     * Returns the params associated with the component
     */
    getParams(){
        return this.__params;
    }

    /**
     * Sets the params associated with the component
     * @param params key -> Parameter Set
     */
    setParams(params){
        this.__params = params;
        //TODO: Modify all the associated Features
        for(let key in params){
            let value = params[key];
            for(let i in this.__features){
                let featureidtochange = this.__features[i];

                //Get the feature id and modify it
                let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
                feature.updateParameter(key, value.getValue());
            }

        }
    }

    /**
     * Returns a paper.Rectangle object that defines the bounds of the component
     * @return {*}
     */
    getBoundingRectangle(){
        if(this.features.length == 0 || this.features == null || this.features == undefined){
            console.error("No features associated with the component");
        }
        let bounds  = null;
        for(let i in this.features){
            let featureid = this.features[i];
            let render = Registry.viewManager.view.getRenderedFeature(featureid);
            if(bounds){
                bounds = bounds.unite(render.bounds);
            }else{
                bounds = render.bounds;
            }
        }

        return bounds;
    }

    /**
     * Updates the coordinates of the component and all the other features
     * @param center
     */
    updateComponetPosition(center){
        this.updateParameter('position', new Parameter('Point', center));
        for(let i in this.__features){
            let featureidtochange = this.__features[i];

            let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
            // feature.updateParameter('position', center);
            feature.updateParameter('position', center);
        }
    }

    /**
     * Replicates the component at the given positions
     * @param xpos Integer location of X
     * @param ypos Integer location of Y
     * @param name
     * @return {Component}
     */
    replicate(xpos, ypos, name = Registry.currentDevice.generateNewName(this.__type)){
        //TODO: Fix this ridiculous chain of converting params back and forth, there should be an easier way
        //Converting all the params into raw values
        // let paramvalues = {};
        // for(let key in this.__params.parameters){
        //     paramvalues[key] = this.getValue(key);
        // }
        let replicaparams = new Params(null, null, null, this.__params.parameters);
        let ret = new Component(this.__type, replicaparams, name, this.__entity);
        console.log("Checking what the new component params are:", ret.__params);
        //Generate New features
        for(let i in this.features){
            let feature = Registry.currentDevice.getFeatureByID(this.features[i]);
            console.log("test", this.getPosition()[0], this.getPosition()[1] ,this.getPosition());
            let replica = feature.replicate(this.getPosition()[0], this.getPosition()[1]);
            ret.features.push(replica.getID());

            //TODO: add new feature to the layer in which the current feature is in
            let currentlayer = Registry.currentDevice.getLayerFromFeatureID(this.features[i]);
            currentlayer.addFeature(replica);

        }
        console.warn("TODO: Generate renders for the new Features for this new component");
        ret.updateComponetPosition([xpos, ypos]);
        return ret;
    }

    /**
     * This method is used to import the component from Interchange V1 JSON
     * @param json
     * @returns {*}
     */
    static fromInterchangeV1(json){
        // let set;
        // if (json.hasOwnProperty("set")) set = json.set;
        // else set = "Basic";
        // //TODO: This will have to change soon when the thing is updated
        // throw new Error("Need to implement Interchange V1 Import for component object");
        let name = json.name;
        let id = json.id;
        let entity = json.entity;
        let params = {};
        for(let key in json.params){
            console.log("key:", key, "value:", json.params[key]);
            let paramobject = Parameter.generateComponentParameter(key, json.params[key]);
            params[key] = paramobject;
        }

        let paramstoadd = new Params(null, null, null, params);

        let component = new Component(entity, paramstoadd, name, entity, id);
        return component;

    }

}

module.exports = Component;<|endoftext|>


<commit_before>(function(){
  'use strict';

  angular.module( 'dolmen' )
  .controller( 'ShowDetailController', function( $http ){
    var list = this;
    list.details = [ ];

    $http.get( 'app/po_dash/request_detail.json' )
    .then( function( response ){
      console.log( response );
      list.details = response.data;
    });
  });
})();

// (function(){
//   'use strict';
//
//   var app = angular.module('dolmen');
//
//       app.controller('DetailController', function($firebaseArray, $location){
//
//       var self = this;
//
//       var firebaseDetail = new Firebase('https://dolmen.firebaseio.com');
//
//       self.data = $firebaseArray(firebaseDetail);
//       console.log(self.data);
//
//       self.display = function() {
//         self.data.$add({
//           date: self.date,
//           category: self.category,
//           address: self.address,
//           tenant: self.tenant,
//           phone: self.phone,
//           email: self.email,
//           description: self.description,
//           instructions: self.instructions,
//           comments: self.comments
//         }).then(function(){
//           $location.path('/details');
//         });
//       };
//     });
//
//     app.controller('ShowDetailController', function($http){
//       var display = this;
//       this.detail = { };
//       // console.log(this.detail);
//
//       this.addDetail = function(spices) {
//         spices.details.push(this.detail);
//         this.detail = { };
//       };
//
//       $http.get('https://dolmen.firebaseio.com/' + '.json').then(function(adobo){
//         display.details = adobo.data;
//       });
//     });
//
//
// });
<commit_msg>added edit on click functionality to ShowDetailController<commit_after>(function(){
  'use strict';

  var app = angular.module( 'dolmen' )
  app.controller( 'ShowDetailController', function( $http, $scope ){
    var list = this;
    list.details = [ ];

    $http.get( 'app/po_dash/request_detail.json' )
    .then( function( response ){
      console.log( response );
      list.details = response.data;
    });
    app.run( function( editableOptions ){
      console.log( editableOptions )
      editableOptions.theme = 'bs3';
    });
    $scope.user = {
      date: 'detail.date',
      category: 'detail.category',
      address: 'detail.address',
      tenant: 'detail.tenant',
      address: 'detail.address',
      phone: 'detail.phone',
      email: 'detail.email',
      description: 'detail.description',
      Comments: 'detail.comments'
    };

  });
})();

// (function(){
//   'use strict';
//
//   var app = angular.module('dolmen');
//
//       app.controller('DetailController', function($firebaseArray, $location){
//
//       var self = this;
//
//       var firebaseDetail = new Firebase('https://dolmen.firebaseio.com');
//
//       self.data = $firebaseArray(firebaseDetail);
//       console.log(self.data);
//
//       self.display = function() {
//         self.data.$add({
//           date: self.date,
//           category: self.category,
//           address: self.address,
//           tenant: self.tenant,
//           phone: self.phone,
//           email: self.email,
//           description: self.description,
//           instructions: self.instructions,
//           comments: self.comments
//         }).then(function(){
//           $location.path('/details');
//         });
//       };
//     });
//
//     app.controller('ShowDetailController', function($http){
//       var display = this;
//       this.detail = { };
//       // console.log(this.detail);
//
//       this.addDetail = function(spices) {
//         spices.details.push(this.detail);
//         this.detail = { };
//       };
//
//       $http.get('https://dolmen.firebaseio.com/' + '.json').then(function(adobo){
//         display.details = adobo.data;
//       });
//     });
//
//
// });
<|endoftext|>


<commit_before>import Promise from 'bluebird';
import { api } from '@steemit/steem-js';
import { ifHive } from 'app/utils/StateFunctions';

import stateCleaner from 'app/redux/stateCleaner';

export async function callBridge(method, params) {
    console.log('call bridge', method, params);
    const call = (method, params, callback) => {
        return api.call('bridge.' + method, params, callback);
    };
    return Promise.promisify(call)(method, params);
}

export async function getStateAsync(url, observer, ssr = false) {
    if (observer === undefined) observer = null;

    const { page, tag, sort, key } = parsePath(url);

    let state = {
        accounts: {},
        community: {},
        content: {},
        discussion_idx: {},
    };

    // load `content` and `discussion_idx`
    if (page == 'posts' || page == 'account') {
        const posts = await loadPosts(sort, tag, observer);
        state['content'] = posts['content'];
        state['discussion_idx'] = posts['discussion_idx'];
    } else if (page == 'thread') {
        const posts = await loadThread(key[0], key[1]);
        state['content'] = posts['content'];
    } else {
        // no-op
    }

    // append `community` key
    if (tag && ifHive(tag)) {
        state['community'][tag] = await callBridge('get_community', {
            name: tag,
            observer: observer,
        });
    }

    if (ssr) {
        // append `topics` key
        state['topics'] = await callBridge('get_trending_topics', { observer });
    }

    const cleansed = stateCleaner(state);
    return cleansed;
}

async function loadThread(account, permlink) {
    const author = account.slice(1);
    posts = await callBridge('get_discussion', { author, permlink });
    return { content: posts };
}

async function loadPosts(sort, tag, observer) {
    const account = tag && tag[0] == '@' ? tag.slice(1) : null;

    let posts;
    if (account)
        posts = await callBridge('get_account_posts', {
            sort,
            account,
            observer,
        });
    else posts = await callBridge('get_ranked_posts', { sort, tag, observer });

    let content = {};
    let keys = [];
    for (var idx in posts) {
        const post = posts[idx];
        const key = post['author'] + '/' + post['permlink'];
        content[key] = post;
        keys.push(key);
    }

    let discussion_idx = {};
    discussion_idx[tag] = {};
    discussion_idx[tag][sort] = keys;

    return { content, discussion_idx };
}

function parsePath(url) {
    // strip off query string
    url = url.split('?')[0];

    // strip off leading and trailing slashes
    if (url.length > 0 && url[0] == '/') url = url.substring(1, url.length);
    if (url.length > 0 && url[url.length - 1] == '/')
        url = url.substring(0, url.length - 1);

    // blank URL defaults to `trending`
    if (url === '') url = 'trending';

    const part = url.split('/');
    const parts = part.length;
    const sorts = [
        'trending',
        'promoted',
        'hot',
        'created',
        'payout',
        'payout_comments',
        'muted',
    ];
    const acct_tabs = ['blog', 'feed', 'comments', 'recent-replies', 'payout'];

    let page = null;
    let tag = null;
    let sort = null;
    let key = null;

    if (parts == 1 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = '';
    } else if (parts == 2 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = part[1];
    } else if (parts == 3 && part[1][0] == '@') {
        page = 'thread';
        tag = part[0];
        key = [part[1], part[2]];
    } else if (parts == 1 && part[0][0] == '@') {
        page = 'account';
        sort = 'blog';
        tag = part[0];
    } else if (parts == 2 && part[0][0] == '@' && acct_tabs.includes(part[1])) {
        page = 'account';
        sort = part[1] == 'recent-replies' ? 'replies' : part[1];
        tag = part[0];
    } else {
        // no-op URL
    }

    return { page, tag, sort, key };
}
<commit_msg>fix undeclared var<commit_after>import Promise from 'bluebird';
import { api } from '@steemit/steem-js';
import { ifHive } from 'app/utils/StateFunctions';

import stateCleaner from 'app/redux/stateCleaner';

export async function callBridge(method, params) {
    console.log('call bridge', method, params);
    const call = (method, params, callback) => {
        return api.call('bridge.' + method, params, callback);
    };
    return Promise.promisify(call)(method, params);
}

export async function getStateAsync(url, observer, ssr = false) {
    if (observer === undefined) observer = null;

    const { page, tag, sort, key } = parsePath(url);

    let state = {
        accounts: {},
        community: {},
        content: {},
        discussion_idx: {},
    };

    // load `content` and `discussion_idx`
    if (page == 'posts' || page == 'account') {
        const posts = await loadPosts(sort, tag, observer);
        state['content'] = posts['content'];
        state['discussion_idx'] = posts['discussion_idx'];
    } else if (page == 'thread') {
        const posts = await loadThread(key[0], key[1]);
        state['content'] = posts['content'];
    } else {
        // no-op
    }

    // append `community` key
    if (tag && ifHive(tag)) {
        state['community'][tag] = await callBridge('get_community', {
            name: tag,
            observer: observer,
        });
    }

    if (ssr) {
        // append `topics` key
        state['topics'] = await callBridge('get_trending_topics', { observer });
    }

    const cleansed = stateCleaner(state);
    return cleansed;
}

async function loadThread(account, permlink) {
    const author = account.slice(1);
    const content = await callBridge('get_discussion', { author, permlink });
    return { content };
}

async function loadPosts(sort, tag, observer) {
    const account = tag && tag[0] == '@' ? tag.slice(1) : null;

    let posts;
    if (account) {
        const params = { sort, account, observer };
        posts = await callBridge('get_account_posts', params);
    } else {
        const params = { sort, tag, observer };
        posts = await callBridge('get_ranked_posts', params);
    }

    let content = {};
    let keys = [];
    for (var idx in posts) {
        const post = posts[idx];
        const key = post['author'] + '/' + post['permlink'];
        content[key] = post;
        keys.push(key);
    }

    let discussion_idx = {};
    discussion_idx[tag] = {};
    discussion_idx[tag][sort] = keys;

    return { content, discussion_idx };
}

function parsePath(url) {
    // strip off query string
    url = url.split('?')[0];

    // strip off leading and trailing slashes
    if (url.length > 0 && url[0] == '/') url = url.substring(1, url.length);
    if (url.length > 0 && url[url.length - 1] == '/')
        url = url.substring(0, url.length - 1);

    // blank URL defaults to `trending`
    if (url === '') url = 'trending';

    const part = url.split('/');
    const parts = part.length;
    const sorts = [
        'trending',
        'promoted',
        'hot',
        'created',
        'payout',
        'payout_comments',
        'muted',
    ];
    const acct_tabs = ['blog', 'feed', 'comments', 'recent-replies', 'payout'];

    let page = null;
    let tag = null;
    let sort = null;
    let key = null;

    if (parts == 1 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = '';
    } else if (parts == 2 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = part[1];
    } else if (parts == 3 && part[1][0] == '@') {
        page = 'thread';
        tag = part[0];
        key = [part[1], part[2]];
    } else if (parts == 1 && part[0][0] == '@') {
        page = 'account';
        sort = 'blog';
        tag = part[0];
    } else if (parts == 2 && part[0][0] == '@' && acct_tabs.includes(part[1])) {
        page = 'account';
        sort = part[1] == 'recent-replies' ? 'replies' : part[1];
        tag = part[0];
    } else {
        // no-op URL
    }

    return { page, tag, sort, key };
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @function assertIsFunction - throws if passed value is not a function
 * @param  {*} value - value to check
 * @return {*} true if value is a function, undefined if it's not
 */
function assertIsFunction(value) {
    var type = typeof value;
    if(type !== 'function') {
        throw new TypeError(type + ' is not a function');
    }

    return true;
}

module.exports = assertIsFunction;<commit_msg>assertIsFunction: also check the prototype chain when determining if something is a function<commit_after>'use strict';

/**
 * @function assertIsFunction - throws if passed value is not a function
 * @param  {*} value - value to check
 * @return {*} true if value is a function, undefined if it's not
 */
function assertIsFunction(value) {
    if (!(value instanceof Function) && typeof type !== 'function') {
        throw new TypeError(value + ' (' + typeof value + ') is not a function');
    }

    return true;
}

module.exports = assertIsFunction;
<|endoftext|>


<commit_before>define(['jquery', 'app/Admin', 'app/Form', 'app/Router', 'app/Translator'], function($, admin, formScript, router, translator) {

  function Button() {
    var self = this;

    this.initDelete = function (form) {
      $(form).find('[data-button][data-type=delete]').click(function () {
        var url = $(form).data('delete');
        if (confirm(translator.trans('form.delete.question'))) {
          admin.openLoadingOverlay();
          $.ajax({
            type: 'POST',
            data: {
              _method: 'DELETE'
            },
            url: url,
            success: function () {
              admin.closeLoadingOverlay();
              $(form).trigger('formSaveAfter', form);
            },
            error: function () {
              admin.closeLoadingOverlay();
              alert(translator.trans('error.occurred'));
            }
          });
        }
      });
    };

    this.initSave = function (form) {
      $(form).find('[data-button][data-type=save]').click(function (event) {
        event.preventDefault();
        $(this).trigger('formSaveBefore', form);

        var $form = $(form);
        var data = $form.serialize();
        var url = $form.attr('action');
        if ($(this).data('route')) {

          if ($(this).data('route-parameters')) {
            parameters = $(this).data('route-parameters');
          } else {
            var parameters = {};
            if ($(this).data('id')) {
              parameters.id = $(this).data('id');
            }
          }
          url = router.generate($(this).data('route'), parameters);
        }

        admin.openLoadingOverlay();
        $.ajax({
          type: 'POST',
          data: data,
          url: url,
          success: function (data) {
            admin.closeLoadingOverlay();
            $(form).trigger('formSaveAfter', form);
          },
          error: function (jqXHR) {
            admin.closeLoadingOverlay();
            var data = JSON.parse(jqXHR.responseText);

            if (data.code == 400) {
              var getErrors = function (data, errors, position) {
                if (typeof errors == 'undefined') {
                  errors = [];
                }
                if (typeof position == 'undefined') {
                  position = '';
                }

                if (data.hasOwnProperty('errors')) {
                  for (var error in data['errors']) {
                    if (data['errors'].hasOwnProperty(error) && typeof data['errors'][error] == 'string') {
                      errors.push([position, data['errors'][error]]);
                    }
                  }
                }

                if (data.hasOwnProperty('children')) {
                  for (var key in data['children']) {
                    if (data['children'].hasOwnProperty(key)) {
                      if (typeof data['children'][key] == 'object') {
                        getErrors(data['children'][key], errors, position + '.' + key);
                      }

                    }
                  }
                }

                return errors
              };

              var errors = getErrors(data.errors);
              var message = '';
              for (var i = 0; i < errors.length; i++) {
                if (message === '') {
                  message += '';
                } else {
                  message += '

';
                }
                if (errors[i][0] == '') {
                  message += errors[i][1];
                } else {
                  message += errors[i][0].substring(errors[i][0].lastIndexOf('.') + 1) + ': ' + errors[i][1];
                }

              }
              message += '';
              admin.overlayMessage(message, admin.MessageType.Error);
              formScript.markInvalidFormElements(form, errors);
            } else {
              admin.overlayMessage(translator.trans('error.occurred'), admin.MessageType.Error);
            }
          }
        });
      });
    };

    this.initPreview = function (form) {
      $(form).find('[data-button][data-type=preview]').click(function (e) {
        e.preventDefault();
        e.stopPropagation();
        var route = $(this).data('route');
        var link = router.generate(route);
        admin.iframeOverlay(form, link, {
          submit: true
        });
      });
    };

    this.initCancel = function (form) {
      $(form).find('[data-button][data-type=cancel]').click(function () {
        admin.overlayClose();
      });
    };

    this.init = function () {
      $(document).on('formOpenAfter', function (event, form) {
        self.initSave(form);
        self.initDelete(form);
        self.initPreview(form);
        self.initCancel(form);
      });
    };

    self.init();
  };

  return new Button();
});

<commit_msg>fix submit on cancel<commit_after>define(['jquery', 'app/Admin', 'app/Form', 'app/Router', 'app/Translator'], function($, admin, formScript, router, translator) {

  function Button() {
    var self = this;

    this.initDelete = function (form) {
      $(form).find('[data-button][data-type=delete]').click(function () {
        var url = $(form).data('delete');
        if (confirm(translator.trans('form.delete.question'))) {
          admin.openLoadingOverlay();
          $.ajax({
            type: 'POST',
            data: {
              _method: 'DELETE'
            },
            url: url,
            success: function () {
              admin.closeLoadingOverlay();
              $(form).trigger('formSaveAfter', form);
            },
            error: function () {
              admin.closeLoadingOverlay();
              alert(translator.trans('error.occurred'));
            }
          });
        }
      });
    };

    this.initSave = function (form) {
      $(form).find('[data-button][data-type=save]').click(function (event) {
        event.preventDefault();
        $(this).trigger('formSaveBefore', form);

        var $form = $(form);
        var data = $form.serialize();
        var url = $form.attr('action');
        if ($(this).data('route')) {

          if ($(this).data('route-parameters')) {
            parameters = $(this).data('route-parameters');
          } else {
            var parameters = {};
            if ($(this).data('id')) {
              parameters.id = $(this).data('id');
            }
          }
          url = router.generate($(this).data('route'), parameters);
        }

        admin.openLoadingOverlay();
        $.ajax({
          type: 'POST',
          data: data,
          url: url,
          success: function (data) {
            admin.closeLoadingOverlay();
            $(form).trigger('formSaveAfter', form);
          },
          error: function (jqXHR) {
            admin.closeLoadingOverlay();
            var data = JSON.parse(jqXHR.responseText);

            if (data.code == 400) {
              var getErrors = function (data, errors, position) {
                if (typeof errors == 'undefined') {
                  errors = [];
                }
                if (typeof position == 'undefined') {
                  position = '';
                }

                if (data.hasOwnProperty('errors')) {
                  for (var error in data['errors']) {
                    if (data['errors'].hasOwnProperty(error) && typeof data['errors'][error] == 'string') {
                      errors.push([position, data['errors'][error]]);
                    }
                  }
                }

                if (data.hasOwnProperty('children')) {
                  for (var key in data['children']) {
                    if (data['children'].hasOwnProperty(key)) {
                      if (typeof data['children'][key] == 'object') {
                        getErrors(data['children'][key], errors, position + '.' + key);
                      }

                    }
                  }
                }

                return errors
              };

              var errors = getErrors(data.errors);
              var message = '';
              for (var i = 0; i < errors.length; i++) {
                if (message === '') {
                  message += '';
                } else {
                  message += '

';
                }
                if (errors[i][0] == '') {
                  message += errors[i][1];
                } else {
                  message += errors[i][0].substring(errors[i][0].lastIndexOf('.') + 1) + ': ' + errors[i][1];
                }

              }
              message += '';
              admin.overlayMessage(message, admin.MessageType.Error);
              formScript.markInvalidFormElements(form, errors);
            } else {
              admin.overlayMessage(translator.trans('error.occurred'), admin.MessageType.Error);
            }
          }
        });
      });
    };

    this.initPreview = function (form) {
      $(form).find('[data-button][data-type=preview]').click(function (e) {
        e.preventDefault();
        e.stopPropagation();
        var route = $(this).data('route');
        var link = router.generate(route);
        admin.iframeOverlay(form, link, {
          submit: true
        });
      });
    };

    this.initCancel = function (form) {
      $(form).find('[data-button][data-type=cancel]').click(function (e) {
        e.preventDefault();
        admin.overlayClose();
      });
    };

    this.init = function () {
      $(document).on('formOpenAfter', function (event, form) {
        self.initSave(form);
        self.initDelete(form);
        self.initPreview(form);
        self.initCancel(form);
      });
    };

    self.init();
  };

  return new Button();
});

<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { Icon, Table, Button } from 'semantic-ui-react'
import { observer, inject } from 'mobx-react'

import MoviesModal from './MoviesModal'
import MoviesModalModel from '../../models/movies/MoviesModalModel'

@inject('MoviesListModel') @observer
class MoviesList extends Component {

    constructor (props) {
        super(props)
        this.store = new this.props.MoviesListModel()
    }

    componentDidMount () {
        this.fetchMovies()
    }

    fetchMovies () {
        this.store.fetchMovies()
    }

    openModal (mode, target) {
        this.store.openModal(mode, target)
    }

    displayModal () {
        const { open } = this.store.modal
        const { target } = this.store.modal
        const { mode } = this.store.modal
        const title = `${mode} a thing!`
        if(open) {
            return this.renderModal(target, mode, title)
        }
    }

    renderModal(target, mode, title){
        const store = new MoviesModalModel({
            target: Object.assign({}, target),
            mode,
            title
        })

        return (
            <MoviesModal
                onClose={()=>{
                    this.store.closeModal()
                    this.fetchMovies()
                }}
                store={store}
            />
        )
    }

    render () {

        const { movies } = this.store

        return (

                {this.displayModal()}

Name

Score

                        {
                            movies.map((movie) => {
                                return(

{movie.name}

{movie.score}

                                )
                            })
                        }

                                <Button
                                    floated='right'
                                    id="add-movie"
                                    icon
                                    labelPosition='left'
                                    primary size='small'
                                    onClick={()=>{
                                        this.openModal('create', {name:''})
                                    }}
                                >
                                    <Icon name='add' id="create-movie"/> Add Movie
                                </Button>

        )
    }
}

export default MoviesList<commit_msg>Changed the title of the modal<commit_after>import React, { Component } from 'react'
import { Icon, Table, Button } from 'semantic-ui-react'
import { observer, inject } from 'mobx-react'

import MoviesModal from './MoviesModal'
import MoviesModalModel from '../../models/movies/MoviesModalModel'

@inject('MoviesListModel') @observer
class MoviesList extends Component {

    constructor (props) {
        super(props)
        this.store = new this.props.MoviesListModel()
    }

    componentDidMount () {
        this.fetchMovies()
    }

    fetchMovies () {
        this.store.fetchMovies()
    }

    openModal (mode, target) {
        this.store.openModal(mode, target)
    }

    displayModal () {
        const { open } = this.store.modal
        const { target } = this.store.modal
        const { mode } = this.store.modal
        const title = `${mode} a movie!`
        if(open) {
            return this.renderModal(target, mode, title)
        }
    }

    renderModal(target, mode, title){
        const store = new MoviesModalModel({
            target: Object.assign({}, target),
            mode,
            title
        })

        return (
            <MoviesModal
                onClose={()=>{
                    this.store.closeModal()
                    this.fetchMovies()
                }}
                store={store}
            />
        )
    }

    render () {

        const { movies } = this.store

        return (

                {this.displayModal()}

Name

Score

                        {
                            movies.map((movie) => {
                                return(

{movie.name}

{movie.score}

                                )
                            })
                        }

                                <Button
                                    floated='right'
                                    id="add-movie"
                                    icon
                                    labelPosition='left'
                                    primary size='small'
                                    onClick={()=>{
                                        this.openModal('create', {name:''})
                                    }}
                                >
                                    <Icon name='add' id="create-movie"/> Add Movie
                                </Button>

        )
    }
}

export default MoviesList<|endoftext|>


<commit_before>window.addEventListener( 'DOMContentLoaded', function () {

  'use strict';

  var CLASS_NAME_CURRENT_TAB = 'CG2-compactNav__item--current';
  var SELECTOR_CURRENT_TAB = '.' + CLASS_NAME_CURRENT_TAB;
  var CLASS_NAME_CURRENT_PANE = 'CG2-tabContent__pane--current';
  var SELECTOR_CURRENT_PANE = '.' + CLASS_NAME_CURRENT_PANE;

  var i, l;
  var $tabs = document.querySelectorAll( '[data-cg2-tab]' );

  // initialize tabs click event
  for ( i = 0, l = $tabs.length; i < l; i++ ) {

    $tabs[i].addEventListener( 'click', onClickTab );

  }

  function onClickTab ( e ) {

    e.preventDefault();

    var $this = e.target;
    var $li = closest( $this, 'li' );
    var $ul = closest( $li, 'ul' );
    var $target = null;
    var selector = $this.getAttribute( 'href' );

    if ( $li.classList.contains( CLASS_NAME_CURRENT_TAB ) ) { return }

    $target = document.querySelector( selector );

    activateTab( $li, $ul );
    activatePane( $target, closest( $target, '.CG2-tabContent' ) );
    syncTab( '[href="' + $this.getAttribute('href') + '"]' );
  }

  // sync current status for other tab set
  function syncTab (selector) {

    var i, l;
    var $tabs = document.querySelectorAll( '[data-cg2-tab]' + selector );

    for ( i = 0, l = $tabs.length; i < l; i++ ) {

      ( function () {

        var $tab = $tabs[i];
        var $li = closest( $tab, 'li' );
        var $ul = closest( $li, 'ul' );

        if ( $li.classList.contains( CLASS_NAME_CURRENT_TAB ) ) { return }

        activateTab( $li, $ul );

      } )();

    }

  }

  // switch current tab
  function activateTab ( element, container ) {

    var $active = container.querySelector( SELECTOR_CURRENT_TAB );

    $active.classList.remove( CLASS_NAME_CURRENT_TAB );
    element.classList.add( CLASS_NAME_CURRENT_TAB );

  }

  // switch current pane
  function activatePane ( element, container ) {

    var $active = container.querySelector( SELECTOR_CURRENT_PANE );

    $active.classList.remove( CLASS_NAME_CURRENT_PANE );
    element.classList.add( CLASS_NAME_CURRENT_PANE );

  }

  // get closest parent node by selector
  function closest (element, selector) {

    var parent = null;

    while ( element !== null ) {

      parent = element.parentElement;

      if ( parent !== null && parent.matches(selector) ) {

        return parent;

      }

      element = parent;

    }

    return null;

  }

} );
<commit_msg>data属性でコントロール、挙動の制御にclassを使わないようにした<commit_after>window.addEventListener( 'DOMContentLoaded', function () {

  'use strict';

  var CLASS_NAME_CURRENT_TAB = 'CG2-compactNav__item--current';
  var SELECTOR_CURRENT_TAB = '.' + CLASS_NAME_CURRENT_TAB;
  var ATTR_NAME_CURRENT_PANE = 'data-cg2-tab-pane';
  var SELECTOR_CURRENT_PANE = '[' + ATTR_NAME_CURRENT_PANE + '="current"]';

  var i, l;
  var $tabs = document.querySelectorAll( '[data-cg2-tab-button]' );

  // initialize tabs click event
  for ( i = 0, l = $tabs.length; i < l; i ++ ) {

    $tabs[ i ].addEventListener( 'click', onClickTab );

  }

  function onClickTab ( e ) {

    e.preventDefault();

    var $this   = e.target;
    var $li     = closest( $this, 'li' );
    var $ul     = closest( $li,   'ul' );
    var $target = null;
    var selector = $this.getAttribute( 'href' );

    if ( $li.classList.contains( CLASS_NAME_CURRENT_TAB ) ) { return }

    $target = document.querySelector( selector );

    activateTab( $li, $ul );
    activatePane( $target, closest( $target, '[data-cg2-tab-content]' ) );
    syncTab( '[href="' + $this.getAttribute( 'href' ) + '"]' );

  }

  // sync current status for other tab set
  function syncTab ( selector ) {

    var i, l;
    var $tabs = document.querySelectorAll( '[data-cg2-tab-button]' + selector );

    for ( i = 0, l = $tabs.length; i < l; i ++ ) {

      ( function () {

        var $tab = $tabs[ i ];
        var $li  = closest( $tab, 'li' );
        var $ul  = closest( $li,  'ul' );

        if ( $li.classList.contains( CLASS_NAME_CURRENT_TAB ) ) { return }

        activateTab( $li, $ul );

      } )();

    }

  }

  // switch current tab
  function activateTab ( element, container ) {

    var $active = container.querySelector( SELECTOR_CURRENT_TAB );

    $active.classList.remove( CLASS_NAME_CURRENT_TAB );
    element.classList.add( CLASS_NAME_CURRENT_TAB );

  }

  // switch current pane
  function activatePane ( element, container ) {

    var $active = container.querySelector( SELECTOR_CURRENT_PANE );

    $active.setAttribute( ATTR_NAME_CURRENT_PANE, '' );
    element.setAttribute( ATTR_NAME_CURRENT_PANE, 'current' );

  }

  // get closest parent node by selector
  function closest ( element, selector ) {

    var parent = null;

    while ( element !== null ) {

      parent = element.parentElement;

      if ( parent !== null && parent.matches( selector ) ) {

        return parent;

      }

      element = parent;

    }

    return null;

  }

} );
<|endoftext|>


<commit_before>'use strict';

var Async = require('async');
var Lodash = require('lodash');
var Request = require('request');

var Browser = require('./browser');
var Extra = require('./extra');
var LocalSourceFolder = require('./local-source');
var LocalCacheFolder = require('./local-cache');
var Hub = require('./hub');

function derefDependencies(depReferenceTable, cb) {
    // TODO - Actually fetch the referenced dependencies (SHAs)
    cb(null, depReferenceTable);
}

function loadDependencies(info, cb) {
    // We're going to accumulate dependencies into this dependency
    // array, recursively loading the dependencies of dependencies, etc.
    var dependenciesWanted = info.dereffedDependencyTable;
    var dependenciesFound = {};
    var loadAttemptActions = [];
    if (this.options.doLoadDependenciesFromBrowser) {
        loadAttemptActions.push(Browser.loadDependenciesFromBrowser.bind(this));
    }
    else {
        if (this.options.localDependenciesSourceFolder) {
            loadAttemptActions.push(LocalSourceFolder.loadDependenciesFromLocalSourceFolder.bind(this, this.options.localDependenciesSourceFolder));
        }
        if (this.options.localDependenciesCacheFolder) {
            loadAttemptActions.push(LocalCacheFolder.loadDependenciesFromLocalCacheFolder.bind(this, this.options.localDependenciesCacheFolder));
        }
    }
    if (this.options.codeManagerHost) {
        loadAttemptActions.push(Hub.loadDependenciesFromHub.bind(this, this.options.codeManagerHost));
    }
    // After attempting to load the dependencies from all of the
    // possible sources, check to see if we succeeded overall
    var composedLoadFn = Async.seq.apply(Async, loadAttemptActions);
    composedLoadFn(dependenciesWanted, dependenciesFound, function(depLoadErr) {
        var dependenciesMissing = Extra.getDependenciesMissing(dependenciesWanted, dependenciesFound);
        if (depLoadErr || Object.keys(dependenciesMissing).length > 0) {
            console.error('Unable to load dependencies ' + JSON.stringify(dependenciesMissing));
        }
        cb(null, dependenciesFound);
    });
}

function saveAssets(where, info, cb) {
    if (where === 'local') {
        if (this.options.localDependenciesSourceFolder) {
            LocalSourceFolder.saveAssets.call(this, this.options.localDependenciesSourceFolder, info, function(localSourceSaveErr, localSourceSaveInfo) {
                cb(null, localSourceSaveInfo);
            });
        }
    }
}

function saveBundle(where, info, cb) {
    if (where === 'local') {
        if (this.options.localDependenciesSourceFolder) {
            LocalSourceFolder.saveBundle.call(this, this.options.localDependenciesSourceFolder, info, function(localSourceSaveErr, localSourceSaveInfo) {
                cb(null, localSourceSaveInfo);
            });
        }
    }
}

module.exports = {
    derefDependencies: derefDependencies,
    loadDependencies: loadDependencies,
    saveAssets: saveAssets,
    saveBundle: saveBundle
};
<commit_msg>A bit more logging when dependencies can't be found<commit_after>'use strict';

var Async = require('async');
var Lodash = require('lodash');
var Request = require('request');

var Browser = require('./browser');
var Extra = require('./extra');
var LocalSourceFolder = require('./local-source');
var LocalCacheFolder = require('./local-cache');
var Hub = require('./hub');

function derefDependencies(depReferenceTable, cb) {
    // TODO - Actually fetch the referenced dependencies (SHAs)
    cb(null, depReferenceTable);
}

function loadDependencies(info, cb) {
    // We're going to accumulate dependencies into this dependency
    // array, recursively loading the dependencies of dependencies, etc.
    var dependenciesWanted = info.dereffedDependencyTable;
    var dependenciesFound = {};
    var loadAttemptActions = [];
    if (this.options.doLoadDependenciesFromBrowser) {
        loadAttemptActions.push(Browser.loadDependenciesFromBrowser.bind(this));
    }
    else {
        if (this.options.localDependenciesSourceFolder) {
            loadAttemptActions.push(LocalSourceFolder.loadDependenciesFromLocalSourceFolder.bind(this, this.options.localDependenciesSourceFolder));
        }
        if (this.options.localDependenciesCacheFolder) {
            loadAttemptActions.push(LocalCacheFolder.loadDependenciesFromLocalCacheFolder.bind(this, this.options.localDependenciesCacheFolder));
        }
    }
    if (this.options.codeManagerHost) {
        loadAttemptActions.push(Hub.loadDependenciesFromHub.bind(this, this.options.codeManagerHost));
    }
    // After attempting to load the dependencies from all of the
    // possible sources, check to see if we succeeded overall
    var composedLoadFn = Async.seq.apply(Async, loadAttemptActions);
    composedLoadFn(dependenciesWanted, dependenciesFound, function(depLoadErr) {
        var dependenciesMissing = Extra.getDependenciesMissing(dependenciesWanted, dependenciesFound);
        if (depLoadErr || Object.keys(dependenciesMissing).length > 0) {
            console.error('Got error when trying to load dependencies:', depLoadErr);
            console.error('Unable to load `' + info.name + '`\'s dependencies ' + JSON.stringify(dependenciesMissing));
        }
        cb(null, dependenciesFound);
    });
}

function saveAssets(where, info, cb) {
    if (where === 'local') {
        if (this.options.localDependenciesSourceFolder) {
            LocalSourceFolder.saveAssets.call(this, this.options.localDependenciesSourceFolder, info, function(localSourceSaveErr, localSourceSaveInfo) {
                cb(null, localSourceSaveInfo);
            });
        }
    }
}

function saveBundle(where, info, cb) {
    if (where === 'local') {
        if (this.options.localDependenciesSourceFolder) {
            LocalSourceFolder.saveBundle.call(this, this.options.localDependenciesSourceFolder, info, function(localSourceSaveErr, localSourceSaveInfo) {
                cb(null, localSourceSaveInfo);
            });
        }
    }
}

module.exports = {
    derefDependencies: derefDependencies,
    loadDependencies: loadDependencies,
    saveAssets: saveAssets,
    saveBundle: saveBundle
};
<|endoftext|>


<commit_before>// Connects the FormBuilder with various sanity roles
import React, {PropTypes} from 'react'
import documentStore from 'part:@sanity/base/datastore/document'
import Spinner from 'part:@sanity/components/loading/spinner'
import DefaultButton from 'part:@sanity/components/buttons/default'
import FormBuilder from 'part:@sanity/form-builder'
import schemaTypePrefix from '../utils/schemaTypePrefix'
import schema from 'part:@sanity/base/schema'
import ReferringDocumentsHelper from '../components/ReferringDocumentsHelper'
import InspectView from '../components/InspectView'
import dataAspects from '../utils/dataAspects'
import {throttle, omit} from 'lodash'
import {withRouterHOC} from 'part:@sanity/base/router'

import styles from './styles/EditorPane.css'
import {Patcher} from '@sanity/mutator'

const preventDefault = ev => ev.preventDefault()

// Want a nicer api for listen/ulinsten
function listen(target, eventType, callback, useCapture = false) {
  target.addEventListener(eventType, callback, useCapture)
  return function unlisten() {
    target.removeEventListener(eventType, callback, useCapture)
  }
}

function getInitialState() {
  return {
    loading: true,
    spin: false,
    deleted: null,
    inspect: false,
    progress: {kind: 'info', message: 'Loading'}
  }
}

export default withRouterHOC(class EditorPane extends React.PureComponent {
  static propTypes = {
    documentId: PropTypes.string,
    typeName: PropTypes.string,
    router: PropTypes.shape({
      state: PropTypes.object
    })
  };

  subscriptions = [];

  state = getInitialState();

  get schemaTypePrefix() {
    if (!this._schemaTypePrefix) {
      this._schemaTypePrefix = schemaTypePrefix(schema)
    }
    return this._schemaTypePrefix
  }

  deserialize(serialized) {
    const {typeName} = this.props
    return serialized
      ? FormBuilder.deserialize(this.schemaTypePrefix.removeFrom(serialized), typeName)
      : FormBuilder.createEmpty(typeName)
  }

  serialize(value) {
    return this.schemaTypePrefix.addTo(value.serialize())
  }

  setupSubscriptions(props) {
    this.tearDownSubscriptions()
    const {documentId} = props

    this.document = documentStore.checkout(documentId)

    const documentEvents = this.document.events.subscribe({
      next: this.handleDocumentEvent,
      error: this.handleDocumentError
    })

    this.subscriptions = [documentEvents]
  }

  handleDocumentError = error => {
    this.setState({progress: {kind: 'error', message: error.message}})
  }

  handleDocumentEvent = event => {
    switch (event.type) {
      case 'snapshot': {
        this.setState({
          loading: false,
          progress: null,
          value: this.deserialize(event.document)
        })
        break
      }
      case 'rebase': {
        this.setState({
          value: this.deserialize(event.document)
        })
        break
      }
      case 'mutate': {
        this.handleIncomingMutationEvent(event)
        break
      }
      case 'create': {
        this.setState({
          value: this.deserialize(event.document)
        })
        break
      }
      default: {
        // eslint-disable-next-line no-console
        console.log('Unhandled document event type "%s"', event.type, event)
      }
    }
  }

  tearDownSubscriptions() {
    this.subscriptions.forEach(sub => sub.unsubscribe())
  }

  componentDidMount() {
    this.setupSubscriptions(this.props)
    this.unlistenForKey = listen(window, 'keypress', event => {
      const shouldToggle = event.ctrlKey
        && event.charCode === 9
        && !event.shiftKey
        && !event.altKey

      if (shouldToggle) {
        this.setState({inspect: !this.state.inspect})
      }
    })
  }

  componentWillUnmount() {
    this.tearDownSubscriptions()
    this.unlistenForKey()
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.documentId !== this.props.documentId) {
      this.setState(getInitialState())
      this.setupSubscriptions(nextProps)
    }
  }

  handleIncomingMutationEvent(event) {
    const {mutations, origin} = event
    const operations = []
    mutations.forEach(mutation => {
      if (mutation.create) {
        operations.push(prev => {
          if (prev) {
            throw new Error('Had an unexpected existing document when receiving a create mutation')
          }
          return this.deserialize(mutation.create)
        })
      } else if (mutation.delete) {
        // Handle deletions after the fact
        if (origin !== 'local') {
          operations.push(() => null)
        }
      } else if (mutation.patch) {
        operations.push(previous => new Patcher(mutation.patch).applyViaAccessor(previous))
      } else {
        // eslint-disable-next-line no-console
        console.error(new Error(`Received unsupported mutation ${JSON.stringify(mutation, null, 2)}`))
      }
    })

    const previousValue = this.state.value
    const nextValue = operations.reduce((prev, operation) => operation(prev), previousValue)

    this.setState({
      deleted: nextValue === null ? this.serialize(previousValue) : null,
      value: nextValue
    })
  }

  handleIncomingDelete(event) {
    const {router} = this.props
    router.navigate(omit(router.state, 'action', 'selectedDocumentId'), {replace: true})
  }

  commit = throttle(() => {
    this.setState({spin: true, progress: null})
    this.document.commit().subscribe({
      next: () => {
        this.setState({progress: {kind: 'success', message: 'Saved…'}})
      },
      error: err => {
        this.setState({progress: {kind: 'error', message: `Save failed ${err.message}`}})
      },
      complete: () => {
        this.setState({spin: false})
      }
    })
  }, 1000, {leading: true, trailing: true})

  handleChange = event => {
    this.document.patch(event.patches)
    this.commit()
  }

  handleReferenceResult = event => {
    if (event.documents.length === 0) {
      this.handleDelete()
    } else {
      this.setState({referringDocuments: event.documents, deleteInProgress: false})
    }
  }

  handleDelete = () => {
    this.setState({progress: {kind: 'info', message: 'Deleting…'}, deleteInProgress: false})
    this.document.delete()
    this.commit()
  }

  handleRequestDelete = () => {
    this.setState({progress: {kind: 'info', message: 'Checking references…'}, deleteInProgress: true})
    const refSubscription = documentStore.query('*[references($docId), limit: 100]', {docId: this.props.documentId}).subscribe({
      next: this.handleReferenceResult,
      error: this.handleReferenceError
    })

    this.subscriptions.push(refSubscription)
  }

  handleCancelDeleteRequest = () => {
    this.setState({progress: null, referringDocuments: null})
  }

  handleRestore = () => {
    const {deleted} = this.state
    this.setState({progress: {kind: 'info', message: 'Restoring…'}})
    this.document.create(deleted)
    this.commit()
  }

  render() {
    const {value, inspect, deleted, loading, spin, validation, deleteInProgress, referringDocuments} = this.state
    const {typeName} = this.props
    const titleProp = dataAspects.getItemDisplayField(typeName)
    const schemaType = schema.get(this.props.typeName)

    if (loading) {
      return (

          <Spinner center message={`Loading ${schemaType.title}…`} />

      )
    }

    if (deleted) {
      return (

Document was deleted

          <DefaultButton onClick={this.handleRestore}>Restore</DefaultButton>

      )
    }

    return (

{schemaType.title}

            {(titleProp && value.getAttribute(titleProp).serialize()) || 'Untitled…'}

            <Spinner />

        <form className={styles.editor} onSubmit={preventDefault} id="Sanity_Default_FormBuilder_ScrollContainer">
          <FormBuilder
            value={value}
            validation={validation}
            onChange={this.handleChange}
          />

            <DefaultButton onClick={this.handleRequestDelete} color="danger" loading={deleteInProgress}>
              Delete {value.getAttribute(titleProp).serialize()}
            </DefaultButton>

        </form>

        {referringDocuments && (
          <ReferringDocumentsHelper
            documents={referringDocuments}
            currentValue={value}
            onCancel={this.handleCancelDeleteRequest}
          />
        )}
        {inspect && (
          <InspectView
            value={value.serialize()}
            onClose={() => this.setState({inspect: false})}
          />
        )}

    )
  }
})
<commit_msg>[desk-tool] Change inspect shortcut to ctrl+alt+i<commit_after>// Connects the FormBuilder with various sanity roles
import React, {PropTypes} from 'react'
import documentStore from 'part:@sanity/base/datastore/document'
import Spinner from 'part:@sanity/components/loading/spinner'
import DefaultButton from 'part:@sanity/components/buttons/default'
import FormBuilder from 'part:@sanity/form-builder'
import schemaTypePrefix from '../utils/schemaTypePrefix'
import schema from 'part:@sanity/base/schema'
import ReferringDocumentsHelper from '../components/ReferringDocumentsHelper'
import InspectView from '../components/InspectView'
import dataAspects from '../utils/dataAspects'
import {throttle, omit} from 'lodash'
import {withRouterHOC} from 'part:@sanity/base/router'

import styles from './styles/EditorPane.css'
import {Patcher} from '@sanity/mutator'

const preventDefault = ev => ev.preventDefault()

// Want a nicer api for listen/ulinsten
function listen(target, eventType, callback, useCapture = false) {
  target.addEventListener(eventType, callback, useCapture)
  return function unlisten() {
    target.removeEventListener(eventType, callback, useCapture)
  }
}

function getInitialState() {
  return {
    loading: true,
    spin: false,
    deleted: null,
    inspect: false,
    progress: {kind: 'info', message: 'Loading'}
  }
}

export default withRouterHOC(class EditorPane extends React.PureComponent {
  static propTypes = {
    documentId: PropTypes.string,
    typeName: PropTypes.string,
    router: PropTypes.shape({
      state: PropTypes.object
    })
  };

  subscriptions = [];

  state = getInitialState();

  get schemaTypePrefix() {
    if (!this._schemaTypePrefix) {
      this._schemaTypePrefix = schemaTypePrefix(schema)
    }
    return this._schemaTypePrefix
  }

  deserialize(serialized) {
    const {typeName} = this.props
    return serialized
      ? FormBuilder.deserialize(this.schemaTypePrefix.removeFrom(serialized), typeName)
      : FormBuilder.createEmpty(typeName)
  }

  serialize(value) {
    return this.schemaTypePrefix.addTo(value.serialize())
  }

  setupSubscriptions(props) {
    this.tearDownSubscriptions()
    const {documentId} = props

    this.document = documentStore.checkout(documentId)

    const documentEvents = this.document.events.subscribe({
      next: this.handleDocumentEvent,
      error: this.handleDocumentError
    })

    this.subscriptions = [documentEvents]
  }

  handleDocumentError = error => {
    this.setState({progress: {kind: 'error', message: error.message}})
  }

  handleDocumentEvent = event => {
    switch (event.type) {
      case 'snapshot': {
        this.setState({
          loading: false,
          progress: null,
          value: this.deserialize(event.document)
        })
        break
      }
      case 'rebase': {
        this.setState({
          value: this.deserialize(event.document)
        })
        break
      }
      case 'mutate': {
        this.handleIncomingMutationEvent(event)
        break
      }
      case 'create': {
        this.setState({
          value: this.deserialize(event.document)
        })
        break
      }
      default: {
        // eslint-disable-next-line no-console
        console.log('Unhandled document event type "%s"', event.type, event)
      }
    }
  }

  tearDownSubscriptions() {
    this.subscriptions.forEach(sub => sub.unsubscribe())
  }

  componentDidMount() {
    this.setupSubscriptions(this.props)
    this.unlistenForKey = listen(window, 'keypress', event => {
      const shouldToggle = event.ctrlKey
        && event.charCode === 9
        && event.altKey
        && !event.shiftKey

      if (shouldToggle) {
        this.setState({inspect: !this.state.inspect})
      }
    })
  }

  componentWillUnmount() {
    this.tearDownSubscriptions()
    this.unlistenForKey()
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.documentId !== this.props.documentId) {
      this.setState(getInitialState())
      this.setupSubscriptions(nextProps)
    }
  }

  handleIncomingMutationEvent(event) {
    const {mutations, origin} = event
    const operations = []
    mutations.forEach(mutation => {
      if (mutation.create) {
        operations.push(prev => {
          if (prev) {
            throw new Error('Had an unexpected existing document when receiving a create mutation')
          }
          return this.deserialize(mutation.create)
        })
      } else if (mutation.delete) {
        // Handle deletions after the fact
        if (origin !== 'local') {
          operations.push(() => null)
        }
      } else if (mutation.patch) {
        operations.push(previous => new Patcher(mutation.patch).applyViaAccessor(previous))
      } else {
        // eslint-disable-next-line no-console
        console.error(new Error(`Received unsupported mutation ${JSON.stringify(mutation, null, 2)}`))
      }
    })

    const previousValue = this.state.value
    const nextValue = operations.reduce((prev, operation) => operation(prev), previousValue)

    this.setState({
      deleted: nextValue === null ? this.serialize(previousValue) : null,
      value: nextValue
    })
  }

  handleIncomingDelete(event) {
    const {router} = this.props
    router.navigate(omit(router.state, 'action', 'selectedDocumentId'), {replace: true})
  }

  commit = throttle(() => {
    this.setState({spin: true, progress: null})
    this.document.commit().subscribe({
      next: () => {
        this.setState({progress: {kind: 'success', message: 'Saved…'}})
      },
      error: err => {
        this.setState({progress: {kind: 'error', message: `Save failed ${err.message}`}})
      },
      complete: () => {
        this.setState({spin: false})
      }
    })
  }, 1000, {leading: true, trailing: true})

  handleChange = event => {
    this.document.patch(event.patches)
    this.commit()
  }

  handleReferenceResult = event => {
    if (event.documents.length === 0) {
      this.handleDelete()
    } else {
      this.setState({referringDocuments: event.documents, deleteInProgress: false})
    }
  }

  handleDelete = () => {
    this.setState({progress: {kind: 'info', message: 'Deleting…'}, deleteInProgress: false})
    this.document.delete()
    this.commit()
  }

  handleRequestDelete = () => {
    this.setState({progress: {kind: 'info', message: 'Checking references…'}, deleteInProgress: true})
    const refSubscription = documentStore.query('*[references($docId), limit: 100]', {docId: this.props.documentId}).subscribe({
      next: this.handleReferenceResult,
      error: this.handleReferenceError
    })

    this.subscriptions.push(refSubscription)
  }

  handleCancelDeleteRequest = () => {
    this.setState({progress: null, referringDocuments: null})
  }

  handleRestore = () => {
    const {deleted} = this.state
    this.setState({progress: {kind: 'info', message: 'Restoring…'}})
    this.document.create(deleted)
    this.commit()
  }

  render() {
    const {value, inspect, deleted, loading, spin, validation, deleteInProgress, referringDocuments} = this.state
    const {typeName} = this.props
    const titleProp = dataAspects.getItemDisplayField(typeName)
    const schemaType = schema.get(this.props.typeName)

    if (loading) {
      return (

          <Spinner center message={`Loading ${schemaType.title}…`} />

      )
    }

    if (deleted) {
      return (

Document was deleted

          <DefaultButton onClick={this.handleRestore}>Restore</DefaultButton>

      )
    }

    return (

{schemaType.title}

            {(titleProp && value.getAttribute(titleProp).serialize()) || 'Untitled…'}

            <Spinner />

        <form className={styles.editor} onSubmit={preventDefault} id="Sanity_Default_FormBuilder_ScrollContainer">
          <FormBuilder
            value={value}
            validation={validation}
            onChange={this.handleChange}
          />

            <DefaultButton onClick={this.handleRequestDelete} color="danger" loading={deleteInProgress}>
              Delete {value.getAttribute(titleProp).serialize()}
            </DefaultButton>

        </form>

        {referringDocuments && (
          <ReferringDocumentsHelper
            documents={referringDocuments}
            currentValue={value}
            onCancel={this.handleCancelDeleteRequest}
          />
        )}
        {inspect && (
          <InspectView
            value={value.serialize()}
            onClose={() => this.setState({inspect: false})}
          />
        )}

    )
  }
})
<|endoftext|>


<commit_before>const fs = require('fs')
const merge = require('lodash.merge')
const stripIndent = require('common-tags/lib/stripIndent')
const utils = require('../server/helpers/utils')
const logger = require('../server/logger')
const crypto = require('crypto')
// @ts-ignore
const { version } = require('../../package.json')

/**
 * Reads all companion configuration set via environment variables
 * and via the config file path
 *
 * @returns {object}
 */
exports.getUppyOptions = () => {
  return merge({}, getConfigFromEnv(), getConfigFromFile())
}

/**
 * Loads the config from environment variables
 *
 * @returns {object}
 */
const getConfigFromEnv = () => {
  const uploadUrls = process.env.COMPANION_UPLOAD_URLS || process.env.UPPYSERVER_UPLOAD_URLS
  const domains = process.env.COMPANION_DOMAINS || process.env.COMPANION_DOMAIN || process.env.UPPYSERVER_DOMAINS || process.env.UPPYSERVER_DOMAIN || null
  const validHosts = domains ? domains.split(',') : []

  return {
    // TODO: Rename providerOptions to providers.
    providerOptions: {
      google: {
        key: process.env.COMPANION_GOOGLE_KEY || process.env.UPPYSERVER_GOOGLE_KEY,
        secret: process.env.COMPANION_GOOGLE_SECRET || process.env.UPPYSERVER_GOOGLE_SECRET
      },
      dropbox: {
        key: process.env.COMPANION_DROPBOX_KEY || process.env.UPPYSERVER_DROPBOX_KEY,
        secret: process.env.COMPANION_DROPBOX_SECRET || process.env.UPPYSERVER_DROPBOX_SECRET
      },
      instagram: {
        key: process.env.COMPANION_INSTAGRAM_KEY || process.env.UPPYSERVER_INSTAGRAM_KEY,
        secret: process.env.COMPANION_INSTAGRAM_SECRET || process.env.UPPYSERVER_INSTAGRAM_SECRET
      },
      s3: {
        key: process.env.COMPANION_AWS_KEY || process.env.UPPYSERVER_AWS_KEY,
        secret: process.env.COMPANION_AWS_SECRET || process.env.UPPYSERVER_AWS_SECRET,
        bucket: process.env.COMPANION_AWS_BUCKET || process.env.UPPYSERVER_AWS_BUCKET,
        endpoint: process.env.COMPANION_AWS_ENDPOINT || process.env.UPPYSERVER_AWS_ENDPOINT,
        region: process.env.COMPANION_AWS_REGION || process.env.UPPYSERVER_AWS_REGION
      }
    },
    server: {
      host: process.env.COMPANION_DOMAIN || process.env.UPPYSERVER_DOMAIN,
      protocol: process.env.COMPANION_PROTOCOL || process.env.UPPYSERVER_PROTOCOL,
      path: process.env.COMPANION_PATH || process.env.UPPYSERVER_PATH,
      implicitPath: process.env.COMPANION_IMPLICIT_PATH || process.env.UPPYSERVER_IMPLICIT_PATH,
      oauthDomain: process.env.COMPANION_OAUTH_DOMAIN || process.env.UPPYSERVER_OAUTH_DOMAIN,
      validHosts: validHosts
    },
    filePath: process.env.COMPANION_DATADIR || process.env.UPPYSERVER_DATADIR,
    redisUrl: process.env.COMPANION_REDIS_URL || process.env.UPPYSERVER_REDIS_URL,
    sendSelfEndpoint: process.env.COMPANION_SELF_ENDPOINT || process.env.UPPYSERVER_SELF_ENDPOINT,
    uploadUrls: uploadUrls ? uploadUrls.split(',') : null,
    secret: process.env.COMPANION_SECRET || process.env.UPPYSERVER_SECRET || generateSecret(),
    debug: process.env.NODE_ENV !== 'production',
    // TODO: this is a temporary hack to support distributed systems.
    // it is not documented, because it should be changed soon.
    cookieDomain: process.env.COMPANION_COOKIE_DOMAIN || process.env.UPPYSERVER_COOKIE_DOMAIN,
    multipleInstances: true
  }
}

/**
 * Auto-generates server secret
 *
 * @returns {string}
 */
const generateSecret = () => {
  logger.warn('auto-generating server secret because none was specified', 'startup.secret')
  return crypto.randomBytes(64).toString('hex')
}

/**
 * Loads the config from a file and returns it as an object
 *
 * @returns {object}
 */
const getConfigFromFile = () => {
  const path = getConfigPath()
  if (!path) return {}

  const rawdata = fs.readFileSync(getConfigPath())
  // TODO validate the json object fields to match the uppy config schema
  // @ts-ignore
  return JSON.parse(rawdata)
}

/**
 * Returns the config path specified via cli arguments
 *
 * @returns {string}
 */
const getConfigPath = () => {
  let configPath

  for (let i = process.argv.length - 1; i >= 0; i--) {
    const isConfigFlag = process.argv[i] === '-c' || process.argv[i] === '--config'
    const flagHasValue = i + 1 <= process.argv.length
    if (isConfigFlag && flagHasValue) {
      configPath = process.argv[i + 1]
      break
    }
  }

  return configPath
}

/**
 * validates that the mandatory companion options are set.
 * If it is invalid, it will console an error of unset options and exits the process.
 * If it is valid, nothing happens.
 *
 * @param {object} config
 */
exports.validateConfig = (config) => {
  const mandatoryOptions = ['secret', 'filePath', 'server.host']
  /** @type {string[]} */
  const unspecified = []

  mandatoryOptions.forEach((i) => {
    const value = i.split('.').reduce((prev, curr) => prev[curr], config)

    if (!value) unspecified.push(`"${i}"`)
  })

  // vaidate that all required config is specified
  if (unspecified.length) {
    console.error('\x1b[31m', 'Please specify the following options',
      'to run companion as Standalone:\n', unspecified.join(',\n'), '\x1b[0m')
    process.exit(1)
  }

  // validate that specified filePath is writeable/readable.
  // TODO: consider moving this into the uppy module itself.
  try {
    // @ts-ignore
    fs.accessSync(`${config.filePath}`, fs.R_OK | fs.W_OK)
  } catch (err) {
    console.error('\x1b[31m', `No access to "${config.filePath}".`,
      'Please ensure the directory exists and with read/write permissions.', '\x1b[0m')
    process.exit(1)
  }
}

/**
 *
 * @param {string} url
 */
exports.hasProtocol = (url) => {
  return url.startsWith('http://') || url.startsWith('https://')
}

exports.buildHelpfulStartupMessage = (uppyOptions) => {
  const buildURL = utils.getURLBuilder(uppyOptions)
  const callbackURLs = []
  Object.keys(uppyOptions.providerOptions).forEach((providerName) => {
    // s3 does not need redirect_uris
    if (providerName === 's3') {
      return
    }

    if (providerName === 'google') {
      providerName = 'drive'
    }

    callbackURLs.push(buildURL(`/${providerName}/callback`, true))
  })

  return stripIndent`
    Welcome to Companion v${version}
    ===================================

    Congratulations on setting up Companion! Thanks for joining our cause, you have taken
    the first step towards the future of file uploading! We
    hope you are as excited about this as we are!

    While you did an awesome job on getting Companion running, this is just the welcome
    message, so let's talk about the places that really matter:

    - Be sure to add ${callbackURLs.join(', ')} as your Oauth redirect uris on their corresponding developer interfaces.
    - The URL ${buildURL('/metrics', true)} is available for  statistics to keep Companion running smoothly
    - https://github.com/transloadit/uppy/issues - report your bugs here

    So quit lollygagging, start uploading and experience the future!
  `
}
<commit_msg>Updated the callback URIs to reflect their correct location<commit_after>const fs = require('fs')
const merge = require('lodash.merge')
const stripIndent = require('common-tags/lib/stripIndent')
const utils = require('../server/helpers/utils')
const logger = require('../server/logger')
const crypto = require('crypto')
// @ts-ignore
const { version } = require('../../package.json')

/**
 * Reads all companion configuration set via environment variables
 * and via the config file path
 *
 * @returns {object}
 */
exports.getUppyOptions = () => {
  return merge({}, getConfigFromEnv(), getConfigFromFile())
}

/**
 * Loads the config from environment variables
 *
 * @returns {object}
 */
const getConfigFromEnv = () => {
  const uploadUrls = process.env.COMPANION_UPLOAD_URLS || process.env.UPPYSERVER_UPLOAD_URLS
  const domains = process.env.COMPANION_DOMAINS || process.env.COMPANION_DOMAIN || process.env.UPPYSERVER_DOMAINS || process.env.UPPYSERVER_DOMAIN || null
  const validHosts = domains ? domains.split(',') : []

  return {
    // TODO: Rename providerOptions to providers.
    providerOptions: {
      google: {
        key: process.env.COMPANION_GOOGLE_KEY || process.env.UPPYSERVER_GOOGLE_KEY,
        secret: process.env.COMPANION_GOOGLE_SECRET || process.env.UPPYSERVER_GOOGLE_SECRET
      },
      dropbox: {
        key: process.env.COMPANION_DROPBOX_KEY || process.env.UPPYSERVER_DROPBOX_KEY,
        secret: process.env.COMPANION_DROPBOX_SECRET || process.env.UPPYSERVER_DROPBOX_SECRET
      },
      instagram: {
        key: process.env.COMPANION_INSTAGRAM_KEY || process.env.UPPYSERVER_INSTAGRAM_KEY,
        secret: process.env.COMPANION_INSTAGRAM_SECRET || process.env.UPPYSERVER_INSTAGRAM_SECRET
      },
      s3: {
        key: process.env.COMPANION_AWS_KEY || process.env.UPPYSERVER_AWS_KEY,
        secret: process.env.COMPANION_AWS_SECRET || process.env.UPPYSERVER_AWS_SECRET,
        bucket: process.env.COMPANION_AWS_BUCKET || process.env.UPPYSERVER_AWS_BUCKET,
        endpoint: process.env.COMPANION_AWS_ENDPOINT || process.env.UPPYSERVER_AWS_ENDPOINT,
        region: process.env.COMPANION_AWS_REGION || process.env.UPPYSERVER_AWS_REGION
      }
    },
    server: {
      host: process.env.COMPANION_DOMAIN || process.env.UPPYSERVER_DOMAIN,
      protocol: process.env.COMPANION_PROTOCOL || process.env.UPPYSERVER_PROTOCOL,
      path: process.env.COMPANION_PATH || process.env.UPPYSERVER_PATH,
      implicitPath: process.env.COMPANION_IMPLICIT_PATH || process.env.UPPYSERVER_IMPLICIT_PATH,
      oauthDomain: process.env.COMPANION_OAUTH_DOMAIN || process.env.UPPYSERVER_OAUTH_DOMAIN,
      validHosts: validHosts
    },
    filePath: process.env.COMPANION_DATADIR || process.env.UPPYSERVER_DATADIR,
    redisUrl: process.env.COMPANION_REDIS_URL || process.env.UPPYSERVER_REDIS_URL,
    sendSelfEndpoint: process.env.COMPANION_SELF_ENDPOINT || process.env.UPPYSERVER_SELF_ENDPOINT,
    uploadUrls: uploadUrls ? uploadUrls.split(',') : null,
    secret: process.env.COMPANION_SECRET || process.env.UPPYSERVER_SECRET || generateSecret(),
    debug: process.env.NODE_ENV !== 'production',
    // TODO: this is a temporary hack to support distributed systems.
    // it is not documented, because it should be changed soon.
    cookieDomain: process.env.COMPANION_COOKIE_DOMAIN || process.env.UPPYSERVER_COOKIE_DOMAIN,
    multipleInstances: true
  }
}

/**
 * Auto-generates server secret
 *
 * @returns {string}
 */
const generateSecret = () => {
  logger.warn('auto-generating server secret because none was specified', 'startup.secret')
  return crypto.randomBytes(64).toString('hex')
}

/**
 * Loads the config from a file and returns it as an object
 *
 * @returns {object}
 */
const getConfigFromFile = () => {
  const path = getConfigPath()
  if (!path) return {}

  const rawdata = fs.readFileSync(getConfigPath())
  // TODO validate the json object fields to match the uppy config schema
  // @ts-ignore
  return JSON.parse(rawdata)
}

/**
 * Returns the config path specified via cli arguments
 *
 * @returns {string}
 */
const getConfigPath = () => {
  let configPath

  for (let i = process.argv.length - 1; i >= 0; i--) {
    const isConfigFlag = process.argv[i] === '-c' || process.argv[i] === '--config'
    const flagHasValue = i + 1 <= process.argv.length
    if (isConfigFlag && flagHasValue) {
      configPath = process.argv[i + 1]
      break
    }
  }

  return configPath
}

/**
 * validates that the mandatory companion options are set.
 * If it is invalid, it will console an error of unset options and exits the process.
 * If it is valid, nothing happens.
 *
 * @param {object} config
 */
exports.validateConfig = (config) => {
  const mandatoryOptions = ['secret', 'filePath', 'server.host']
  /** @type {string[]} */
  const unspecified = []

  mandatoryOptions.forEach((i) => {
    const value = i.split('.').reduce((prev, curr) => prev[curr], config)

    if (!value) unspecified.push(`"${i}"`)
  })

  // vaidate that all required config is specified
  if (unspecified.length) {
    console.error('\x1b[31m', 'Please specify the following options',
      'to run companion as Standalone:\n', unspecified.join(',\n'), '\x1b[0m')
    process.exit(1)
  }

  // validate that specified filePath is writeable/readable.
  // TODO: consider moving this into the uppy module itself.
  try {
    // @ts-ignore
    fs.accessSync(`${config.filePath}`, fs.R_OK | fs.W_OK)
  } catch (err) {
    console.error('\x1b[31m', `No access to "${config.filePath}".`,
      'Please ensure the directory exists and with read/write permissions.', '\x1b[0m')
    process.exit(1)
  }
}

/**
 *
 * @param {string} url
 */
exports.hasProtocol = (url) => {
  return url.startsWith('http://') || url.startsWith('https://')
}

exports.buildHelpfulStartupMessage = (uppyOptions) => {
  const buildURL = utils.getURLBuilder(uppyOptions)
  const callbackURLs = []
  Object.keys(uppyOptions.providerOptions).forEach((providerName) => {
    // s3 does not need redirect_uris
    if (providerName === 's3') {
      return
    }

    if (providerName === 'google') {
      providerName = 'drive'
    }

    callbackURLs.push(buildURL(`/connect/${providerName}/callback`, true))
  })

  return stripIndent`
    Welcome to Companion v${version}
    ===================================

    Congratulations on setting up Companion! Thanks for joining our cause, you have taken
    the first step towards the future of file uploading! We
    hope you are as excited about this as we are!

    While you did an awesome job on getting Companion running, this is just the welcome
    message, so let's talk about the places that really matter:

    - Be sure to add ${callbackURLs.join(', ')} as your Oauth redirect uris on their corresponding developer interfaces.
    - The URL ${buildURL('/metrics', true)} is available for  statistics to keep Companion running smoothly
    - https://github.com/transloadit/uppy/issues - report your bugs here

    So quit lollygagging, start uploading and experience the future!
  `
}
<|endoftext|>