<commit_before>/* global AFRAME Blob uploadcare */

var saveAs = require('../../vendor/saveas.js').saveAs;

AFRAME.registerSystem('painter', {
  init: function () {

    var mappings = {
      behaviours: {},
      mappings: {
        painting: {
          common: {
            'grip.down': 'undo',
            'trigger.changed': 'paint'
          },

          'vive-controls': {
            'axis.move': 'changeBrushSizeInc',
            'trackpad.touchstart': 'startChangeBrushSize',
            'menu.down': 'toggleMenu',

            // Teleport
            'trackpad.down': 'aim',
            'trackpad.up': 'teleport'
          },

          'oculus-touch-controls': {
            'axis.move': 'changeBrushSizeAbs',
            'abutton.down': 'toggleMenu',
            'xbutton.down': 'toggleMenu',

            // Teleport
            'ybutton.down': 'aim',
            'ybutton.up': 'teleport',

            'bbutton.down': 'aim',
            'bbutton.up': 'teleport'
          },

          'windows-motion-controls': {
            'axis.move': 'changeBrushSizeAbs',
            'menu.down': 'toggleMenu',

            // Teleport
            'trackpad.down': 'aim',
            'trackpad.up': 'teleport'
          },
        }
      }
    };

    this.sceneEl.addEventListener('loaded', function() {
      AFRAME.registerInputMappings(mappings);
      AFRAME.currentInputMapping = 'painting';
    });

    this.version = '1.2';
    this.brushSystem = this.sceneEl.systems.brush;
    this.showTemplateItems = true;

    function getUrlParams () {
      var match;
      var pl = /\+/g;  // Regex for replacing addition symbol with a space
      var search = /([^&=]+)=?([^&]*)/g;
      var decode = function (s) { return decodeURIComponent(s.replace(pl, ' ')); };
      var query = window.location.search.substring(1);
      var urlParams = {};

      match = search.exec(query);
      while (match) {
        urlParams[decode(match[1])] = decode(match[2]);
        match = search.exec(query);
      }
      return urlParams;
    }
    var urlParams = getUrlParams();
    if (urlParams.url || urlParams.urljson) {
      var isBinary = urlParams.urljson === undefined;
      this.brushSystem.loadFromUrl(urlParams.url || urlParams.urljson, isBinary);
      document.getElementById('logo').setAttribute('visible', false);
      document.getElementById('acamera').setAttribute('orbit-controls', 'position', '0 1.6 3');
      document.getElementById('apainter-logo').classList.remove('hidden');
      //document.getElementById('apainter-author').classList.remove('hidden'); // not used yet
    }

    if (urlParams.bgcolor !== undefined) {
      document.body.style.backgroundColor = '#' + urlParams.bgcolor;
    }
    if (urlParams.sky !== undefined) {
      this.sceneEl.addEventListener('loaded', function (evt) {
        if (urlParams.sky === '') {
          document.getElementById('sky').setAttribute('visible', false);
        } else {
          document.getElementById('sky').setAttribute('material', 'src', urlParams.sky);
        }
      });
    }
    if (urlParams.floor !== undefined) {
      this.sceneEl.addEventListener('loaded', function (evt) {
        if (urlParams.floor === '') {
          document.getElementById('ground').setAttribute('visible', false);
        } else {
          document.getElementById('ground').setAttribute('material', 'src', urlParams.floor);
        }
      });
    }

    this.startedPainting = false;
    var self = this;
    document.addEventListener('stroke-started', function (event) {
      if (!self.startedPainting) {
        const logo = document.getElementById('logo');
        logo.emit('started-painting');
        self.startedPainting = true;
      }
    });

    // @fixme This is just for debug until we'll get some UI
    document.addEventListener('keyup', function (event) {
      if(event.shiftKey || event.ctrlKey) return;
      if (event.keyCode === 8) {
        // Undo (Backspace)
        self.brushSystem.undo();
      }
      if (event.keyCode === 67) {
        // Clear (c)
        self.brushSystem.clear();
      }
      if (event.keyCode === 71) {
        // Export to GTF (g)
        var drawing = document.querySelector('.a-drawing');
        self.sceneEl.systems['gltf-exporter'].export(drawing);
      }
      if (event.keyCode === 78) {
        // Next brush (n)
        var hands = document.querySelectorAll('[paint-controls]');
        var brushesNames = Object.keys(AFRAME.BRUSHES);
        var index = brushesNames.indexOf(hands[0].components.brush.data.brush);
        index = (index + 1) % brushesNames.length;
        [].forEach.call(hands, function (hand) {
          hand.setAttribute('brush', 'brush', brushesNames[index]);
        });
      }

      if (event.keyCode === 84) {
        // Random stroke (t)
        self.brushSystem.generateTestLines();
      }

      if (event.keyCode === 82) {
        // Random stroke (r)
        self.brushSystem.generateRandomStrokes(1);
      }
      if (event.keyCode === 76) {
        // load binary from file (l)
        self.brushSystem.loadFromUrl('demo.apa', true);
      }
      if (event.keyCode === 85) { // u - upload
        self.upload();
      }
      if (event.keyCode === 86) { // v - save
        self.save();
      }
      if (event.keyCode === 74) { // j - save json
        self.saveJSON();
      }
      if (event.keyCode === 79) { // o - toggle template objects+images visibility
        self.showTemplateItems = !self.showTemplateItems;
        var templateItems = document.querySelectorAll('.templateitem');
        for (var i = 0; i < templateItems.length; i++) {
            templateItems[i].setAttribute('visible', self.showTemplateItems);
        }
      }
      if (event.keyCode === 88) { // x remove 2nd
        self.brushSystem.removeById(2);
      }
    });

    console.info('A-PAINTER Version: ' + this.version);
  },
  saveJSON: function () {
    var json = this.brushSystem.getJSON();
    var blob = new Blob([JSON.stringify(json)], {type: 'application/json'});
    saveAs(blob, 'demo.json');
  },
  save: function () {
    var dataviews = this.brushSystem.getBinary();
    var blob = new Blob(dataviews, {type: 'application/octet-binary'});
    saveAs(blob, 'demo.apa');
  },
  upload: function (success, error) {
    this.sceneEl.emit('drawing-upload-started');
    var self = this;

    var baseUrl = 'https://aframe.io/a-painter/?url=';

    var dataviews = this.brushSystem.getBinary();
    var blob = new Blob(dataviews, {type: 'application/octet-binary'});
    var uploader = 'uploadcare'; // or 'fileio'
    if (uploader === 'fileio') {
      // Using file.io
      var fd = new window.FormData();
      fd.append('file', blob);
      var xhr = new window.XMLHttpRequest();
      xhr.open('POST', 'https://file.io'); // ?expires=1y
      xhr.onreadystatechange = function (data) {
        if (xhr.readyState === 4) {
          var response = JSON.parse(xhr.response);
          if (response.success) {
            console.log('Uploaded link: ', baseUrl + response.link);
            self.sceneEl.emit('drawing-upload-completed', {url: baseUrl + response.link});
            if (success) { success(); }
          }
        } else {
          self.sceneEl.emit('drawing-upload-error', {errorInfo: null, fileInfo: null});
          if (error) { error(); }
        }
      };
      xhr.send(fd);
    } else {
      var file = uploadcare.fileFrom('object', blob);
      file.done(function (fileInfo) {
        console.log('Uploaded link: ', baseUrl + fileInfo.cdnUrl);
        self.sceneEl.emit('drawing-upload-completed', {url: baseUrl + fileInfo.cdnUrl});
        if (success) { success(); }
      }).fail(function (errorInfo, fileInfo) {
        self.sceneEl.emit('drawing-upload-error', {errorInfo: errorInfo, fileInfo: fileInfo});
        if (error) { error(errorInfo); }
      }).progress(function (uploadInfo) {
        self.sceneEl.emit('drawing-upload-progress', {progress: uploadInfo.progress});
      });
    }
  }
});
<commit_msg>logo -> logoEl<commit_after>/* global AFRAME Blob uploadcare */

var saveAs = require('../../vendor/saveas.js').saveAs;

AFRAME.registerSystem('painter', {
  init: function () {

    var mappings = {
      behaviours: {},
      mappings: {
        painting: {
          common: {
            'grip.down': 'undo',
            'trigger.changed': 'paint'
          },

          'vive-controls': {
            'axis.move': 'changeBrushSizeInc',
            'trackpad.touchstart': 'startChangeBrushSize',
            'menu.down': 'toggleMenu',

            // Teleport
            'trackpad.down': 'aim',
            'trackpad.up': 'teleport'
          },

          'oculus-touch-controls': {
            'axis.move': 'changeBrushSizeAbs',
            'abutton.down': 'toggleMenu',
            'xbutton.down': 'toggleMenu',

            // Teleport
            'ybutton.down': 'aim',
            'ybutton.up': 'teleport',

            'bbutton.down': 'aim',
            'bbutton.up': 'teleport'
          },

          'windows-motion-controls': {
            'axis.move': 'changeBrushSizeAbs',
            'menu.down': 'toggleMenu',

            // Teleport
            'trackpad.down': 'aim',
            'trackpad.up': 'teleport'
          },
        }
      }
    };

    this.sceneEl.addEventListener('loaded', function() {
      AFRAME.registerInputMappings(mappings);
      AFRAME.currentInputMapping = 'painting';
    });

    this.version = '1.2';
    this.brushSystem = this.sceneEl.systems.brush;
    this.showTemplateItems = true;

    function getUrlParams () {
      var match;
      var pl = /\+/g;  // Regex for replacing addition symbol with a space
      var search = /([^&=]+)=?([^&]*)/g;
      var decode = function (s) { return decodeURIComponent(s.replace(pl, ' ')); };
      var query = window.location.search.substring(1);
      var urlParams = {};

      match = search.exec(query);
      while (match) {
        urlParams[decode(match[1])] = decode(match[2]);
        match = search.exec(query);
      }
      return urlParams;
    }
    var urlParams = getUrlParams();
    if (urlParams.url || urlParams.urljson) {
      var isBinary = urlParams.urljson === undefined;
      this.brushSystem.loadFromUrl(urlParams.url || urlParams.urljson, isBinary);
      document.getElementById('logo').setAttribute('visible', false);
      document.getElementById('acamera').setAttribute('orbit-controls', 'position', '0 1.6 3');
      document.getElementById('apainter-logo').classList.remove('hidden');
      //document.getElementById('apainter-author').classList.remove('hidden'); // not used yet
    }

    if (urlParams.bgcolor !== undefined) {
      document.body.style.backgroundColor = '#' + urlParams.bgcolor;
    }
    if (urlParams.sky !== undefined) {
      this.sceneEl.addEventListener('loaded', function (evt) {
        if (urlParams.sky === '') {
          document.getElementById('sky').setAttribute('visible', false);
        } else {
          document.getElementById('sky').setAttribute('material', 'src', urlParams.sky);
        }
      });
    }
    if (urlParams.floor !== undefined) {
      this.sceneEl.addEventListener('loaded', function (evt) {
        if (urlParams.floor === '') {
          document.getElementById('ground').setAttribute('visible', false);
        } else {
          document.getElementById('ground').setAttribute('material', 'src', urlParams.floor);
        }
      });
    }

    this.startedPainting = false;
    var self = this;
    document.addEventListener('stroke-started', function (event) {
      if (!self.startedPainting) {
        const logoEl = document.getElementById('logo');
        logoEl.emit('started-painting');
        self.startedPainting = true;
      }
    });

    // @fixme This is just for debug until we'll get some UI
    document.addEventListener('keyup', function (event) {
      if(event.shiftKey || event.ctrlKey) return;
      if (event.keyCode === 8) {
        // Undo (Backspace)
        self.brushSystem.undo();
      }
      if (event.keyCode === 67) {
        // Clear (c)
        self.brushSystem.clear();
      }
      if (event.keyCode === 71) {
        // Export to GTF (g)
        var drawing = document.querySelector('.a-drawing');
        self.sceneEl.systems['gltf-exporter'].export(drawing);
      }
      if (event.keyCode === 78) {
        // Next brush (n)
        var hands = document.querySelectorAll('[paint-controls]');
        var brushesNames = Object.keys(AFRAME.BRUSHES);
        var index = brushesNames.indexOf(hands[0].components.brush.data.brush);
        index = (index + 1) % brushesNames.length;
        [].forEach.call(hands, function (hand) {
          hand.setAttribute('brush', 'brush', brushesNames[index]);
        });
      }

      if (event.keyCode === 84) {
        // Random stroke (t)
        self.brushSystem.generateTestLines();
      }

      if (event.keyCode === 82) {
        // Random stroke (r)
        self.brushSystem.generateRandomStrokes(1);
      }
      if (event.keyCode === 76) {
        // load binary from file (l)
        self.brushSystem.loadFromUrl('demo.apa', true);
      }
      if (event.keyCode === 85) { // u - upload
        self.upload();
      }
      if (event.keyCode === 86) { // v - save
        self.save();
      }
      if (event.keyCode === 74) { // j - save json
        self.saveJSON();
      }
      if (event.keyCode === 79) { // o - toggle template objects+images visibility
        self.showTemplateItems = !self.showTemplateItems;
        var templateItems = document.querySelectorAll('.templateitem');
        for (var i = 0; i < templateItems.length; i++) {
            templateItems[i].setAttribute('visible', self.showTemplateItems);
        }
      }
      if (event.keyCode === 88) { // x remove 2nd
        self.brushSystem.removeById(2);
      }
    });

    console.info('A-PAINTER Version: ' + this.version);
  },
  saveJSON: function () {
    var json = this.brushSystem.getJSON();
    var blob = new Blob([JSON.stringify(json)], {type: 'application/json'});
    saveAs(blob, 'demo.json');
  },
  save: function () {
    var dataviews = this.brushSystem.getBinary();
    var blob = new Blob(dataviews, {type: 'application/octet-binary'});
    saveAs(blob, 'demo.apa');
  },
  upload: function (success, error) {
    this.sceneEl.emit('drawing-upload-started');
    var self = this;

    var baseUrl = 'https://aframe.io/a-painter/?url=';

    var dataviews = this.brushSystem.getBinary();
    var blob = new Blob(dataviews, {type: 'application/octet-binary'});
    var uploader = 'uploadcare'; // or 'fileio'
    if (uploader === 'fileio') {
      // Using file.io
      var fd = new window.FormData();
      fd.append('file', blob);
      var xhr = new window.XMLHttpRequest();
      xhr.open('POST', 'https://file.io'); // ?expires=1y
      xhr.onreadystatechange = function (data) {
        if (xhr.readyState === 4) {
          var response = JSON.parse(xhr.response);
          if (response.success) {
            console.log('Uploaded link: ', baseUrl + response.link);
            self.sceneEl.emit('drawing-upload-completed', {url: baseUrl + response.link});
            if (success) { success(); }
          }
        } else {
          self.sceneEl.emit('drawing-upload-error', {errorInfo: null, fileInfo: null});
          if (error) { error(); }
        }
      };
      xhr.send(fd);
    } else {
      var file = uploadcare.fileFrom('object', blob);
      file.done(function (fileInfo) {
        console.log('Uploaded link: ', baseUrl + fileInfo.cdnUrl);
        self.sceneEl.emit('drawing-upload-completed', {url: baseUrl + fileInfo.cdnUrl});
        if (success) { success(); }
      }).fail(function (errorInfo, fileInfo) {
        self.sceneEl.emit('drawing-upload-error', {errorInfo: errorInfo, fileInfo: fileInfo});
        if (error) { error(errorInfo); }
      }).progress(function (uploadInfo) {
        self.sceneEl.emit('drawing-upload-progress', {progress: uploadInfo.progress});
      });
    }
  }
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at http://www.mozillapopcorn.org/butter-license.txt */

define( [
          "core/logger",
          "./media"
        ],
        function(
          Logger,
          Media
        ){

  var Timeline = function( butter, options ){

    var _media = {},
        _currentMedia,
        _parentElement = document.createElement( "div" );

    _parentElement.id = "butter-timeline";

    _parentElement.classList.add( "fadable" );

    this._start = function( onModuleReady ){
      butter.ui.areas.work.addComponent( _parentElement, {
        states: [ "timeline" ],
        transitionIn: function(){
          _parentElement.style.display = "block";
          setTimeout(function(){
            _parentElement.style.opacity = "1";
          }, 0);
        },
        transitionInComplete: function(){

        },
        transitionOut: function(){
          _parentElement.style.opacity = "0";
        },
        transitionOutComplete: function(){
          _parentElement.style.display = "none";
        }
      });

      butter.ui.registerStateToggleFunctions( "timeline", {
        transitionIn: function(){
          _parentElement.removeAttribute( "data-butter-disabled" );
        },
        transitionOut: function(){
          _parentElement.setAttribute( "data-butter-disabled", true );
        }
      });

      butter.ui.pushContentState( "timeline" );
      onModuleReady();
    };

    if( butter.ui ){
      butter.ui.listen( "uivisibilitychanged", function( e ){
        for( var m in _media ){
          if( _media.hasOwnProperty( m ) ){
            _media[ m ].shrunken = !e.data;
          } //if
        } //for
      });
    } //if

    this.findAbsolutePosition = function( obj ){
      var curleft = 0,
          curtop = 0;

      if( obj.offsetParent ) {
        do {
          curleft += obj.offsetLeft;
          curtop += obj.offsetTop;
        } while ( ( obj = obj.offsetParent ) );
      }
      //returns an array
      return [ curleft, curtop ];
    }; //findAbsolutePosition

    butter.listen( "mediaadded", function( event ){
      var mediaObject = event.data,
          media = new Media( butter, mediaObject );

      _media[ mediaObject.id ] = media;
      _parentElement.appendChild( media.element );

      function mediaChanged( event ){
        if ( _currentMedia !== _media[ event.data.id ] ){
          if ( _currentMedia ) {
            _currentMedia.hide();
          }
          _currentMedia = _media[ event.data.id ];
          if ( _currentMedia ) {
            _currentMedia.show();
          }
          butter.dispatch( "timelineready" );
        }
      }

      function mediaRemoved( event ){
        var mediaObject = event.data;
        if( _media[ mediaObject.id ] ){
          _media[ mediaObject.id ].destroy();
        }
        delete _media[ mediaObject.id ];
        if( _currentMedia && ( mediaObject.id === _currentMedia.media.id ) ){
          _currentMedia = undefined;
        }
        butter.unlisten( "mediachanged", mediaChanged );
        butter.unlisten( "mediaremoved", mediaRemoved );
      } //mediaRemoved

      butter.listen( "mediachanged", mediaChanged );
      butter.listen( "mediaremoved", mediaRemoved );
    });

    Object.defineProperties( this, {
      zoom: {
        get: function(){
          return _currentMedia.zoom;
        },
        set: function( val ){
          _currentMedia.zoom = val;
        }
      }
    });

  }; //Timeline

  Timeline.__moduleName = "timeline";

  return Timeline;
}); //define
<commit_msg>[#1387] Used Bobby's solution for division by 0 error.<commit_after>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at http://www.mozillapopcorn.org/butter-license.txt */

define( [
          "core/logger",
          "./media"
        ],
        function(
          Logger,
          Media
        ){

  var Timeline = function( butter, options ){

    var _media = {},
        _currentMedia,
        _parentElement = document.createElement( "div" );

    _parentElement.id = "butter-timeline";

    _parentElement.classList.add( "fadable" );

    this._start = function( onModuleReady ){
      butter.ui.areas.work.addComponent( _parentElement, {
        states: [ "timeline" ],
        transitionIn: function(){
          _parentElement.style.visibility = "visible";
          setTimeout(function(){
            _parentElement.style.opacity = "1";
          }, 0);
        },
        transitionInComplete: function(){

        },
        transitionOut: function(){
          _parentElement.style.opacity = "0";
        },
        transitionOutComplete: function(){
          _parentElement.style.visibility = "hidden";
        }
      });

      butter.ui.registerStateToggleFunctions( "timeline", {
        transitionIn: function(){
          _parentElement.removeAttribute( "data-butter-disabled" );
        },
        transitionOut: function(){
          _parentElement.setAttribute( "data-butter-disabled", true );
        }
      });

      butter.ui.pushContentState( "timeline" );
      onModuleReady();
    };

    if( butter.ui ){
      butter.ui.listen( "uivisibilitychanged", function( e ){
        for( var m in _media ){
          if( _media.hasOwnProperty( m ) ){
            _media[ m ].shrunken = !e.data;
          } //if
        } //for
      });
    } //if

    this.findAbsolutePosition = function( obj ){
      var curleft = 0,
          curtop = 0;

      if( obj.offsetParent ) {
        do {
          curleft += obj.offsetLeft;
          curtop += obj.offsetTop;
        } while ( ( obj = obj.offsetParent ) );
      }
      //returns an array
      return [ curleft, curtop ];
    }; //findAbsolutePosition

    butter.listen( "mediaadded", function( event ){
      var mediaObject = event.data,
          media = new Media( butter, mediaObject );

      _media[ mediaObject.id ] = media;
      _parentElement.appendChild( media.element );

      function mediaChanged( event ){
        if ( _currentMedia !== _media[ event.data.id ] ){
          if ( _currentMedia ) {
            _currentMedia.hide();
          }
          _currentMedia = _media[ event.data.id ];
          if ( _currentMedia ) {
            _currentMedia.show();
          }
          butter.dispatch( "timelineready" );
        }
      }

      function mediaRemoved( event ){
        var mediaObject = event.data;
        if( _media[ mediaObject.id ] ){
          _media[ mediaObject.id ].destroy();
        }
        delete _media[ mediaObject.id ];
        if( _currentMedia && ( mediaObject.id === _currentMedia.media.id ) ){
          _currentMedia = undefined;
        }
        butter.unlisten( "mediachanged", mediaChanged );
        butter.unlisten( "mediaremoved", mediaRemoved );
      } //mediaRemoved

      butter.listen( "mediachanged", mediaChanged );
      butter.listen( "mediaremoved", mediaRemoved );
    });

    Object.defineProperties( this, {
      zoom: {
        get: function(){
          return _currentMedia.zoom;
        },
        set: function( val ){
          _currentMedia.zoom = val;
        }
      }
    });

  }; //Timeline

  Timeline.__moduleName = "timeline";

  return Timeline;
}); //define
<|endoftext|>


<commit_before>var DISCORD = (function(){
  var regexMessageRequest = /\/channels\/(\d+)\/messages[^a-z]/;

  return {
    /*
     * Sets up a callback hook to trigger whenever a message request returns a response (the callback is given the channel ID and message array).
     */
    setupMessageRequestHook: function(callback){
      HOOKS.onAjaxResponse(function(args, req){
        var match = args[1].match(regexMessageRequest);

        if (match){
          var channel = match[1];
          var messages = JSON.parse(req.response);

          callback(channel, messages);
        }
      });
    },

    /*
     * Returns an object containing the selected server name, selected channel name and ID, and the object type.
     * For types DM and GROUP, the server and channel names are identical.
     * For SERVER type, the channel has to be in view, otherwise Discord unloads it.
     */
    getSelectedChannel: function(){
      var obj;

      var channelListEle = DOM.cls("private-channels")[0];
      var channel;

      if (channelListEle){
        channel = DOM.cls("selected", channelListEle)[0];

        if (!channel || !channel.classList.contains("private")){
          return null;
        }
        else{
          var linkSplit = DOM.tag("a", channel)[0].getAttribute("href").split("/");
          var name = [].find.call(DOM.cls("channel-name", channel)[0].childNodes, node => node.nodeType === Node.TEXT_NODE).nodeValue;

          obj = {
            server: name,
            channel: name,
            id: linkSplit[linkSplit.length-1],
            type: DOM.cls("status", channel).length ? "DM" : "GROUP"
          };
        }
      }
      else{
        channelListEle = DOM.cls("guild-channels")[0];
        channel = channelListEle && DOM.cls("selected", channelListEle)[0];

        if (!channel){
          return null;
        }
        else{
          var linkSplit = DOM.tag("a", channel)[0].getAttribute("href").split("/");

          obj = {
            server: DOM.tag("span", DOM.cls("guild-header")[0])[0].innerHTML,
            channel: DOM.cls("channel-name", DOM.cls("selected", channelListEle)[0])[0].innerHTML,
            id: linkSplit[linkSplit.length-1],
            type: "SERVER"
          };
        }
      }

      return obj.channel.length === 0 ? null : obj;
    }
  };
})();
<commit_msg>Add loadOlderMessages function to discord.js<commit_after>var DISCORD = (function(){
  var regexMessageRequest = /\/channels\/(\d+)\/messages[^a-z]/;

  return {
    /*
     * Sets up a callback hook to trigger whenever a message request returns a response (the callback is given the channel ID and message array).
     */
    setupMessageRequestHook: function(callback){
      HOOKS.onAjaxResponse(function(args, req){
        var match = args[1].match(regexMessageRequest);

        if (match){
          var channel = match[1];
          var messages = JSON.parse(req.response);

          callback(channel, messages);
        }
      });
    },

    /*
     * Returns an object containing the selected server name, selected channel name and ID, and the object type.
     * For types DM and GROUP, the server and channel names are identical.
     * For SERVER type, the channel has to be in view, otherwise Discord unloads it.
     */
    getSelectedChannel: function(){
      var obj;

      var channelListEle = DOM.cls("private-channels")[0];
      var channel;

      if (channelListEle){
        channel = DOM.cls("selected", channelListEle)[0];

        if (!channel || !channel.classList.contains("private")){
          return null;
        }
        else{
          var linkSplit = DOM.tag("a", channel)[0].getAttribute("href").split("/");
          var name = [].find.call(DOM.cls("channel-name", channel)[0].childNodes, node => node.nodeType === Node.TEXT_NODE).nodeValue;

          obj = {
            server: name,
            channel: name,
            id: linkSplit[linkSplit.length-1],
            type: DOM.cls("status", channel).length ? "DM" : "GROUP"
          };
        }
      }
      else{
        channelListEle = DOM.cls("guild-channels")[0];
        channel = channelListEle && DOM.cls("selected", channelListEle)[0];

        if (!channel){
          return null;
        }
        else{
          var linkSplit = DOM.tag("a", channel)[0].getAttribute("href").split("/");

          obj = {
            server: DOM.tag("span", DOM.cls("guild-header")[0])[0].innerHTML,
            channel: DOM.cls("channel-name", DOM.cls("selected", channelListEle)[0])[0].innerHTML,
            id: linkSplit[linkSplit.length-1],
            type: "SERVER"
          };
        }
      }

      return obj.channel.length === 0 ? null : obj;
    },

    /*
     * Forces the message column to scroll all the way up to load older messages.
     */
    loadOlderMessages: function(){
      DOM.cls("messages")[0].scrollTop = 0;
    }
  };
})();
<|endoftext|>


<commit_before>;(function ($) {
  function isRightClick (e) {
    var WHICH_RIGHT_CLICK = 3
    var BUTTON_RIGHT_CLICK = 2
    e = e || window.event
    return e.which === WHICH_RIGHT_CLICK || e.button === BUTTON_RIGHT_CLICK || false
  }

  function getSelectedText () {
    return window.getSelection().toString()
  }

  var CONST = {
    WORDS: {
      MAX: 107,
      MIN: 3
    }
  }

  function getQuoteFromText (text) {
    if (text.length > CONST.WORDS.MAX + 2) {
      text = text.substring(0, CONST.WORDS.MAX + 5)
      text += '...'
    }

    return '"' + text + '"'
  }

  function determinateParams (params) {
    params = params || {}
    params.selector = params.selector || 'body'
    params.minimumTextSelected = params.minimumTextSelected || CONST.WORDS.MIN
    params.shareClass = params.shareClass || '.tweet-selection'
    params.height = params.height.toString() || '300'
    params.width = params.width.toString() || '600'
    return params
  }

  $.tweetSelection = function (params) {
    params = determinateParams(params)
    var info = { mouse: {}, isVisible: false }

    function removeShare () {
      $(params.shareClass).remove()
      document.getSelection().removeAllRanges()
    }

    function addShare (text, event) {
      var boxVerticalPosition = info.mouse.top - 60
      var boxHorizontalPosition = info.mouse.left + (event.clientX - info.mouse.left) / 2

      // basic query parameters
      var tag = '<a href="https://twitter.com/share?url=' + encodeURIComponent(window.location.href)
      tag += '&text=' + encodeURIComponent(text)

      // optional query parameters
      ;['hashtags', 'via', 'related'].forEach(function (optional) {
        if (params[optional]) tag += '&' + optional + '=' + encodeURIComponent(params[optional])
      })

      tag += '" class=\'' + params.shareClass.substr(1) + " fade-in one'"
      tag += ' onclick="window.open(this.href, \'\', \'menubar=no,toolbar=no,resizable=yes,'
      tag += 'scrollbars=yes,height=' + params.height
      tag += ',width=' + params.width + '\');return false;">'

      $('body').append(tag)

      $(params.shareClass).css({
        position: 'absolute',
        top: boxVerticalPosition,
        left: boxHorizontalPosition
      })
    }

    // actions when the user starts the selection
    $(params.selector).mousedown(function (event) {
      // take the position of the mouse where the user starts the selection
      // we need this for showing the share button in the middle of the selection
      info.mouse.top = event.clientY + window.pageYOffset
      info.mouse.left = event.clientX

      // remove share button and the old selection
      // Just if the user clicks the left button of the mouse.
      // For right click we must show the genuine browser menu.
      if (!isRightClick(event) && info.isVisible) {
        removeShare()
        info.isVisible = false
      }
    })

    // actions when the user ends the selection
    $(params.selector).mouseup(function (event) {
      var textSelected = getSelectedText()

      // go further just if user click is left mouse click and the selection length is grater than 3 characters
      if (textSelected.length > params.minimumTextSelected && !isRightClick(event)) {
        addShare(getQuoteFromText(textSelected), event)
        info.isVisible = true
      }
    })
  }
})($)
<commit_msg>Fix toString<commit_after>;(function ($) {
  function isRightClick (e) {
    var WHICH_RIGHT_CLICK = 3
    var BUTTON_RIGHT_CLICK = 2
    e = e || window.event
    return e.which === WHICH_RIGHT_CLICK || e.button === BUTTON_RIGHT_CLICK || false
  }

  function getSelectedText () {
    return window.getSelection().toString()
  }

  var CONST = {
    WORDS: {
      MAX: 107,
      MIN: 3
    }
  }

  function getQuoteFromText (text) {
    if (text.length > CONST.WORDS.MAX + 2) {
      text = text.substring(0, CONST.WORDS.MAX + 5)
      text += '...'
    }

    return '"' + text + '"'
  }

  function determinateParams (params) {
    params = params || {}
    params.selector = params.selector || 'body'
    params.minimumTextSelected = params.minimumTextSelected || CONST.WORDS.MIN
    params.shareClass = params.shareClass || '.tweet-selection'
    params.height = (params.height || 300).toString()
    params.width = (params.width || 600).toString()
    return params
  }

  $.tweetSelection = function (params) {
    params = determinateParams(params)
    var info = { mouse: {}, isVisible: false }

    function removeShare () {
      $(params.shareClass).remove()
      document.getSelection().removeAllRanges()
    }

    function addShare (text, event) {
      var boxVerticalPosition = info.mouse.top - 60
      var boxHorizontalPosition = info.mouse.left + (event.clientX - info.mouse.left) / 2

      // basic query parameters
      var tag = '<a href="https://twitter.com/share?url=' + encodeURIComponent(window.location.href)
      tag += '&text=' + encodeURIComponent(text)

      // optional query parameters
      ;['hashtags', 'via', 'related'].forEach(function (optional) {
        if (params[optional]) tag += '&' + optional + '=' + encodeURIComponent(params[optional])
      })

      tag += '" class=\'' + params.shareClass.substr(1) + " fade-in one'"
      tag += ' onclick="window.open(this.href, \'\', \'menubar=no,toolbar=no,resizable=yes,'
      tag += 'scrollbars=yes,height=' + params.height
      tag += ',width=' + params.width + '\');return false;">'

      $('body').append(tag)

      $(params.shareClass).css({
        position: 'absolute',
        top: boxVerticalPosition,
        left: boxHorizontalPosition
      })
    }

    // actions when the user starts the selection
    $(params.selector).mousedown(function (event) {
      // take the position of the mouse where the user starts the selection
      // we need this for showing the share button in the middle of the selection
      info.mouse.top = event.clientY + window.pageYOffset
      info.mouse.left = event.clientX

      // remove share button and the old selection
      // Just if the user clicks the left button of the mouse.
      // For right click we must show the genuine browser menu.
      if (!isRightClick(event) && info.isVisible) {
        removeShare()
        info.isVisible = false
      }
    })

    // actions when the user ends the selection
    $(params.selector).mouseup(function (event) {
      var textSelected = getSelectedText()

      // go further just if user click is left mouse click and the selection length is grater than 3 characters
      if (textSelected.length > params.minimumTextSelected && !isRightClick(event)) {
        addShare(getQuoteFromText(textSelected), event)
        info.isVisible = true
      }
    })
  }
})($)
<|endoftext|>


<commit_before>/**
 * generic table
 *
 * this class creates a HTML table based on Table model (see below) and modify it based on model changes
 *
 * usage example:
 *
      var table = new Table({
          model: table
      });

      $('body').append(table.render().el);

  * model should be a collection of Rows

 */

/**
 * represents a table row
 */
cdb.ui.common.Row = Backbone.Model.extend({
});

cdb.ui.common.TableData = Backbone.Collection.extend({
    model: cdb.ui.common.Row,

    /**
     * get value for row index and columnName
     */
    getCell: function(index, columnName) {
      var r = this.at(index);
      if(!r) {
        return null;
      }
      return r.get(columnName);
    }

});

/**
 * contains information about the table, mainly the schema
 */
cdb.ui.common.TableProperties = Backbone.Model.extend({

  columnNames: function() {
    return _.map(this.get('schema'), function(c) {
      return c[0];
    });
  },

  columnName: function(idx) {
    return this.columnNames()[idx];
  }
});

/**
 * renders a table row
 */
cdb.ui.common.RowView = cdb.core.View.extend({
  tagName: 'tr',

  initialize: function() {
    this.model.bind('change', this.render, this);
    this.model.bind('destroy', this.clean, this);
    this.model.bind('remove', this.clean, this);
    this.add_related_model(this.model);
    this.order = this.options.order;
  },

  valueView: function(colName, value) {
    return value;
  },

  render: function() {
    var self = this;
    var tr = this.$el;
    tr.html('');
    var row = this.model;
    tr.attr('id', 'row_' + row.id);

    var tdIndex = 0;
    if(this.options.row_header) {
        var td = $('<td>');
        td.append(self.valueView('', ''));
        td.attr('data-x', tdIndex);
        tdIndex++;
        tr.append(td);
    }

    var attrs = this.order || _.keys(row.attributes);
    _(attrs).each(function(key) {
      var value = row.attributes[key];
      if(value !== undefined) {
        var td = $('<td>');
        td.attr('id', 'cell_' + row.id + '_' + key);
        td.attr('data-x', tdIndex);
        tdIndex++;
        td.append(self.valueView(key, value));
        tr.append(td);
      }
    });
    return this;
  },

  getCell: function(x) {
    var childNo = x;
    if(this.options.row_header) {
      ++x;
    }
    return this.$('td:eq(' + x + ')');
  },

  getTableView: function() {
    return this.tableView;
  }

});

/**
 * render a table
 * this widget needs two data sources
 * - the table model which contains information about the table (columns and so on). See TableProperties
 * - the model with the data itself (TableData)
 */
cdb.ui.common.Table = cdb.core.View.extend({

  tagName: 'table',
  rowView: cdb.ui.common.RowView,

  events: {
      'click td': '_cellClick',
      'dblclick td': '_cellDblClick'
  },

  default_options: {
  },

  initialize: function() {
    _.defaults(this.options, this.default_options);
    this.dataModel = this.options.dataModel;
    this.rowViews = [];

    // binding
    this.setDataSource(this.dataModel);
    this.model.bind('change', this.render, this);
    this.model.bind('change:dataSource', this.setDataSource, this);

    // assert the rows are removed when table is removed
    this.bind('clean', this.clear_rows, this);

    // prepare for cleaning
    this.add_related_model(this.dataModel);
    this.add_related_model(this.model);
  },

  headerView: function(column) {
      return column[0];
  },

  setDataSource: function(dm) {
    if(this.dataModel) {
      this.dataModel.unbind(null, null, this);
    }
    this.dataModel = dm;
    this.dataModel.bind('reset', this._renderRows, this);
    this.dataModel.bind('add', this.addRow, this);
  },

  _renderHeader: function() {
    var self = this;
    var thead = $("<thead>");
    var tr = $("<tr>");
    if(this.options.row_header) {
      tr.append($("<th>").append(self.headerView(['', 'header'])));
    }
    _(this.model.get('schema')).each(function(col) {
      tr.append($("<th>").append(self.headerView(col)));
    });
    thead.append(tr);
    return thead;
  },

  /**
   * remove all rows
   */
  clear_rows: function() {
    while(this.rowViews.length) {
      // each element removes itself from rowViews
      this.rowViews[0].clean();
    }
    this.rowViews = [];
  },

  /**
   * add rows
   */
  addRow: function(row, collection, options) {
    var self = this;
    var tr = new self.rowView({
      model: row,
      order: this.model.columnNames(),
      row_header: this.options.row_header
    });
    tr.tableView = this;

    tr.bind('clean', function() {
      var idx = _.indexOf(self.rowViews,this);
      self.rowViews.splice(idx, 1);
      // update index
      for(var i = idx; i < self.rowViews.length; ++i) {
        self.rowViews[i].$el.attr('data-y', i);
      }
    });

    tr.render();
    if(options && options.index !== undefined && options.index != self.rowViews.length) {

      tr.$el.insertBefore(self.rowViews[options.index].$el);
      self.rowViews.splice(options.index, 0, tr);
      //tr.$el.attr('data-y', options.index);
      // change others view data-y attribute
      for(var i = options.index; i < self.rowViews.length; ++i) {
        self.rowViews[i].$el.attr('data-y', i);
      }
    } else {
      // at the end
      tr.$el.attr('data-y', self.rowViews.length);
      self.$el.append(tr.el);
      self.rowViews.push(tr);
    }
  },

  /**
   * render only data rows
   */
  _renderRows: function() {
    var self = this;
    this.clear_rows();
    if(this.dataModel.length > 0) {
      this.dataModel.each(function(row) {
        self.addRow(row);
      });
    } else {
      this.addEmptyTableInfo();
    }
  },

  addEmptyTableInfo: function() {
    // #to be overwrite by descendant classes
  },

  /**
   * render table
   */
  render: function() {
    var self = this;

    self.$el.html('');

    // render header
    self.$el.append(self._renderHeader());

    // render data
    self._renderRows();

    return this;

  },

  /**
   * return jquery cell element of cell x,y
   */
  getCell: function(x, y) {
    if(this.options.row_header) {
      ++y;
    }
    return this.rowViews[y].getCell(x);
  },

  _cellClick: function(e, evtName) {
    evtName = evtName || 'cellClick';
    e.preventDefault();
    var cell = $(e.currentTarget || e.target);
    var x = parseInt(cell.attr('data-x'), 10);
    var y = parseInt(cell.parent().attr('data-y'), 10);
    this.trigger(evtName, e, cell, x, y);
  },

  _cellDblClick: function(e) {
    this._cellClick(e, 'cellDblClick');
  }

});
<commit_msg>support for empty table view elements<commit_after>/**
 * generic table
 *
 * this class creates a HTML table based on Table model (see below) and modify it based on model changes
 *
 * usage example:
 *
      var table = new Table({
          model: table
      });

      $('body').append(table.render().el);

  * model should be a collection of Rows

 */

/**
 * represents a table row
 */
cdb.ui.common.Row = Backbone.Model.extend({
});

cdb.ui.common.TableData = Backbone.Collection.extend({
    model: cdb.ui.common.Row,

    /**
     * get value for row index and columnName
     */
    getCell: function(index, columnName) {
      var r = this.at(index);
      if(!r) {
        return null;
      }
      return r.get(columnName);
    }

});

/**
 * contains information about the table, mainly the schema
 */
cdb.ui.common.TableProperties = Backbone.Model.extend({

  columnNames: function() {
    return _.map(this.get('schema'), function(c) {
      return c[0];
    });
  },

  columnName: function(idx) {
    return this.columnNames()[idx];
  }
});

/**
 * renders a table row
 */
cdb.ui.common.RowView = cdb.core.View.extend({
  tagName: 'tr',

  initialize: function() {
    this.model.bind('change', this.render, this);
    this.model.bind('destroy', this.clean, this);
    this.model.bind('remove', this.clean, this);
    this.add_related_model(this.model);
    this.order = this.options.order;
  },

  valueView: function(colName, value) {
    return value;
  },

  render: function() {
    var self = this;
    var tr = this.$el;
    tr.html('');
    var row = this.model;
    tr.attr('id', 'row_' + row.id);

    var tdIndex = 0;
    if(this.options.row_header) {
        var td = $('<td>');
        td.append(self.valueView('', ''));
        td.attr('data-x', tdIndex);
        tdIndex++;
        tr.append(td);
    }

    var attrs = this.order || _.keys(row.attributes);
    _(attrs).each(function(key) {
      var value = row.attributes[key];
      if(value !== undefined) {
        var td = $('<td>');
        td.attr('id', 'cell_' + row.id + '_' + key);
        td.attr('data-x', tdIndex);
        tdIndex++;
        td.append(self.valueView(key, value));
        tr.append(td);
      }
    });
    return this;
  },

  getCell: function(x) {
    var childNo = x;
    if(this.options.row_header) {
      ++x;
    }
    return this.$('td:eq(' + x + ')');
  },

  getTableView: function() {
    return this.tableView;
  }

});

/**
 * render a table
 * this widget needs two data sources
 * - the table model which contains information about the table (columns and so on). See TableProperties
 * - the model with the data itself (TableData)
 */
cdb.ui.common.Table = cdb.core.View.extend({

  tagName: 'table',
  rowView: cdb.ui.common.RowView,

  events: {
      'click td': '_cellClick',
      'dblclick td': '_cellDblClick'
  },

  default_options: {
  },

  initialize: function() {
    _.defaults(this.options, this.default_options);
    this.dataModel = this.options.dataModel;
    this.rowViews = [];

    // binding
    this.setDataSource(this.dataModel);
    this.model.bind('change', this.render, this);
    this.model.bind('change:dataSource', this.setDataSource, this);

    // assert the rows are removed when table is removed
    this.bind('clean', this.clear_rows, this);

    // prepare for cleaning
    this.add_related_model(this.dataModel);
    this.add_related_model(this.model);
  },

  headerView: function(column) {
      return column[0];
  },

  setDataSource: function(dm) {
    if(this.dataModel) {
      this.dataModel.unbind(null, null, this);
    }
    this.dataModel = dm;
    this.dataModel.bind('reset', this._renderRows, this);
    this.dataModel.bind('add', this.addRow, this);
  },

  _renderHeader: function() {
    var self = this;
    var thead = $("<thead>");
    var tr = $("<tr>");
    if(this.options.row_header) {
      tr.append($("<th>").append(self.headerView(['', 'header'])));
    }
    _(this.model.get('schema')).each(function(col) {
      tr.append($("<th>").append(self.headerView(col)));
    });
    thead.append(tr);
    return thead;
  },

  /**
   * remove all rows
   */
  clear_rows: function() {
    this.$('tfoot').remove();
    this.$('tr.noRows').remove();

    while(this.rowViews.length) {
      // each element removes itself from rowViews
      this.rowViews[0].clean();
    }
    this.rowViews = [];
  },

  /**
   * add rows
   */
  addRow: function(row, collection, options) {
    var self = this;
    var tr = new self.rowView({
      model: row,
      order: this.model.columnNames(),
      row_header: this.options.row_header
    });
    tr.tableView = this;

    tr.bind('clean', function() {
      var idx = _.indexOf(self.rowViews,this);
      self.rowViews.splice(idx, 1);
      // update index
      for(var i = idx; i < self.rowViews.length; ++i) {
        self.rowViews[i].$el.attr('data-y', i);
      }
    });

    tr.render();
    if(options && options.index !== undefined && options.index != self.rowViews.length) {

      tr.$el.insertBefore(self.rowViews[options.index].$el);
      self.rowViews.splice(options.index, 0, tr);
      //tr.$el.attr('data-y', options.index);
      // change others view data-y attribute
      for(var i = options.index; i < self.rowViews.length; ++i) {
        self.rowViews[i].$el.attr('data-y', i);
      }
    } else {
      // at the end
      tr.$el.attr('data-y', self.rowViews.length);
      self.$el.append(tr.el);
      self.rowViews.push(tr);
    }
  },

  /**
  * Checks if the table is empty
  * @method isEmptyTable
  * @returns boolean
  */
  isEmptyTable: function() {
    return (this.dataModel.length === 0)
  },

  /**
   * render only data rows
   */
  _renderRows: function() {
    if(! this.isEmptyTable()) {
      var self = this;
      this.clear_rows();

      this.dataModel.each(function(row) {
        self.addRow(row);
      });
    } else {
      this.addEmptyTableInfo();
    }

  },

  /**
  * Method for the children to redefine with the table behaviour when it has no rows.
  * @method addEmptyTableInfo
  * @abstract
  */
  addEmptyTableInfo: function() {
    // #to be overwrite by descendant classes
  },

  /**
   * render table
   */
  render: function() {
    var self = this;

    self.$el.html('');

    // render header
    self.$el.append(self._renderHeader());

    // render data
    self._renderRows();

    return this;

  },

  /**
   * return jquery cell element of cell x,y
   */
  getCell: function(x, y) {
    if(this.options.row_header) {
      ++y;
    }
    return this.rowViews[y].getCell(x);
  },

  _cellClick: function(e, evtName) {
    evtName = evtName || 'cellClick';
    e.preventDefault();
    var cell = $(e.currentTarget || e.target);
    var x = parseInt(cell.attr('data-x'), 10);
    var y = parseInt(cell.parent().attr('data-y'), 10);
    this.trigger(evtName, e, cell, x, y);
  },

  _cellDblClick: function(e) {
    this._cellClick(e, 'cellDblClick');
  }

});
<|endoftext|>


<commit_before>import {call, put, takeLatest} from 'redux-saga/effects';
import {createAction} from 'redux-actions';
import {Record, Map, List} from 'immutable';
import {REHYDRATE} from 'redux-persist/constants';
import * as authCloud from './cloud';

// --- model

class UserInfo extends Record({
  id: undefined,
  email: undefined,
  emailVerified: undefined,
  mobilePhoneNumber: undefined,
  mobilePhoneVerified: undefined,
  authData: undefined,
  username: undefined,            // unused for now, maybe for wechat id later
  nickname: undefined,            // wechat nickname
  avatar: undefined,
  sex: undefined,                 // gender
  language: undefined,
  country: undefined,
  province: undefined,
  city: undefined,
  idNumber: undefined,
  idName: undefined,              // real name
  createdAt: undefined,
  updatedAt: undefined,
  type: undefined,                // user type, e.g. system admin, platform admin, etc.
  roles: List(),
}, 'UserInfo') {
  static fromJson(j) {
    let info = new UserInfo();

    return info.withMutations((m) => {
      m.set('id', j.objectId);
      m.set('email', j.email);
      m.set('emailVerified', j.emailVerified);
      m.set('mobilePhoneNumber', j.mobilePhoneNumber);
      m.set('mobilePhoneVerified', j.mobilePhoneVerified);
      m.set('authData', j.authData);
      m.set('username', j.username);
      m.set('nickname', j.nickname);
      m.set('avatar', j.avatar);
      m.set('sex', j.sex);
      m.set('language', j.language);
      m.set('country', j.country);
      m.set('province', j.province);
      m.set('city', j.city);
      m.set('idNumber', j.idNumber);
      m.set('idName', j.idName);
      m.set('createdAt', j.createdAt);
      m.set('updatedAt', j.updatedAt);
      m.set('type', j.type);
      m.set('roles', new List(j.roles));
    });
  }
}

class RoleInfo extends Record({
  roleId: undefined,
  displayName: undefined,
}, 'RoleInfo') {
  static fromJson(j) {
  }
}

class PermissionInfo extends Record({
  permissionId: undefined,
  displayName: undefined,
}, 'PermissionInfo') {
  static fromJson(j) {

  }
}

class AuthState extends Record({
  loaded: false,              // whether auto login has finished
  activeUserId: undefined,    // current login user
  token: undefined,           // current login user token
  profiles: Map(),            // cached user info list
}, 'AuthState') {

}

// --- constant

const LOADED = 'AUTH@LOADED';
const LOGIN_WITH_MOBILE_PHONE = 'LOGIN_WITH_MOBILE_PHONE';
const AUTO_LOGIN = 'AUTO_LOGIN';
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
const LOGOUT = 'LOGOUT';
const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';

// --- action

export const authAction = {
  loginWithMobilePhone: createAction(LOGIN_WITH_MOBILE_PHONE),
  autoLogin: createAction(AUTO_LOGIN),
  logout: createAction(LOGOUT),
  loginSuccess: createAction(LOGIN_SUCCESS),
};

const loaded = createAction(LOADED);
const logoutSuccess = createAction(LOGOUT_SUCCESS);

// --- saga

function* sagaLoginWithMobilePhone(action) {
  const payload = action.payload;

  try {
    // result user: {
    //   userInfo,
    //   token
    // }
    const user = yield call(authCloud.loginWithMobilePhone, payload);

    const info = UserInfo.fromJsonApi(user.userInfo);

    yield put(authAction.loginSuccess({userInfo: info, token: user.token}));

    if (payload.onSuccess) {
      payload.onSuccess();
    }

    console.log('login succeeded：', info);
  } catch (e) {
    console.log('login failed：', e);
  }
}

function* sagaAutoLogin(action) {
  const payload = action.payload;

  try {
    const user = yield call(authCloud.become, payload);

    const info = UserInfo.fromJsonApi(user.userInfo);

    yield put(authAction.loginSuccess({userInfo: info, token: user.token}));

    console.log('auto login succeeded：', info);
  } catch(e) {
    console.log('auto login failed：', e);
  }

  yield put(loaded({}));
}

function* sagaLogout(action) {
  const payload = action.payload;

  try {
    yield call(authCloud.logout, payload);
  } catch (e) {
  }

  yield put(logoutSuccess({}));

  if (payload.onSuccess) {
    payload.onSuccess();
  }
}

export const authSaga = [
  takeLatest(LOGIN_WITH_MOBILE_PHONE, sagaLoginWithMobilePhone),
  takeLatest(AUTO_LOGIN, sagaAutoLogin),
  takeLatest(LOGOUT, sagaLogout),
];

// --- reducer

const initialState = new AuthState();

export function authReducer(state=initialState, action) {
  switch(action.type) {
    case LOADED:
      return reduceLoaded(state, action);
    case LOGIN_SUCCESS:
      return reduceLoginSuccess(state, action);
    case LOGOUT_SUCCESS:
      return reduceLogoutSuccess(state, action);
    case REHYDRATE:
      return reduceRehydrate(state, action);
    default:
      return state
  }
}

function reduceLoaded(state, action) {
  return state.set('loaded', true);
}

function reduceLoginSuccess(state, action) {
  const {userInfo, token} = action.payload;

  return state.withMutations((m) => {
    m.set('activeUserId', userInfo.id);
    m.set('token', token);
    m.setIn(['profiles', userInfo.id], userInfo);
  });
}

function reduceLogoutSuccess(state, action) {
  const activeUserId = state.get('activeUserId');

  return state.withMutations((m) => {
    m.set('activeUserId', undefined);
    m.set('token', undefined);
    m.deleteIn(['profiles', activeUserId]);
  });
}

function reduceRehydrate(state, action) {
  const incoming = action.payload.AUTH;

  if (!incoming) {
    return state;
  }

  if (!incoming.activeUserId) {
    return state;
  }

  // always re-auth with the server
  // state = state.set('activeUserId', incoming.activeUserId);
  state = state.set('token', incoming.token);

  // convert from plain json dict to immutable.js Map
  const profiles = Map(incoming.profiles);
  try {
    for (let [id, profile] of profiles) {
      if (id && profile) {
        const userInfo = new UserInfo({...profile});
        state = state.setIn(['profiles', id], userInfo);
      }
    }
  } catch (e) {
    profiles.clear();
  }

  return state;
}

// --- selector

function selectLoaded(appState) {
  return appState.AUTH.loaded;
}

function selectActiveUserId(appState) {
  return appState.AUTH.activeUserId;
}

function selectActiveUserIdAndToken(appState) {
  return {
    activeUserId: appState.AUTH.activeUserId,
    token: appState.AUTH.token
  };
}

function selectIsUserLoggedIn(appState) {
  const activeUserId = selectActiveUserIdAndToken(appState).activeUserId;
  return activeUserId !== undefined;
}

function selectUserInfoById(appState, id) {
  return appState.AUTH.getIn(['profiles', id], undefined);
}

function selectUserInfoByIds(appState, ids) {
  let infos = [];

  ids.forEach((id) => {
    const info = selectUserInfoById(appState, id);

    // TODO: invalid user id ?
    if (info !== undefined) {
      infos.push(info.toJS());
    }
  });

  return infos;
}

function selectActiveUserInfo(appState) {
  const activeUserId = selectActiveUserId(appState);

  return activeUserId !== undefined ? appState.AUTH.selectUserInfoById(activeUserId) : undefined;
}

function selectToken(appState) {
  let AUTH = appState.AUTH;
  return AUTH.token;
}

export const authSelector = {
  selectLoaded,
  selectActiveUserId,
  selectIsUserLoggedIn,
  selectUserInfoById,
  selectUserInfoByIds,
  selectActiveUserInfo,
  selectToken,
};
<commit_msg>rename AuthState.profiles to AuthState.users<commit_after>import {call, put, takeLatest} from 'redux-saga/effects';
import {createAction} from 'redux-actions';
import {Record, Map, List} from 'immutable';
import {REHYDRATE} from 'redux-persist/constants';
import * as authCloud from './cloud';

// --- model

class UserInfo extends Record({
  id: undefined,
  email: undefined,
  emailVerified: undefined,
  mobilePhoneNumber: undefined,
  mobilePhoneVerified: undefined,
  authData: undefined,
  username: undefined,            // unused for now, maybe for wechat id later
  nickname: undefined,            // wechat nickname
  avatar: undefined,
  sex: undefined,                 // gender
  language: undefined,
  country: undefined,
  province: undefined,
  city: undefined,
  idNumber: undefined,
  idName: undefined,              // real name
  createdAt: undefined,
  updatedAt: undefined,
  type: undefined,                // user type, e.g. system admin, platform admin, etc.
  roles: List(),
}, 'UserInfo') {
  static fromJson(j) {
    let info = new UserInfo();

    return info.withMutations((m) => {
      m.set('id', j.objectId);
      m.set('email', j.email);
      m.set('emailVerified', j.emailVerified);
      m.set('mobilePhoneNumber', j.mobilePhoneNumber);
      m.set('mobilePhoneVerified', j.mobilePhoneVerified);
      m.set('authData', j.authData);
      m.set('username', j.username);
      m.set('nickname', j.nickname);
      m.set('avatar', j.avatar);
      m.set('sex', j.sex);
      m.set('language', j.language);
      m.set('country', j.country);
      m.set('province', j.province);
      m.set('city', j.city);
      m.set('idNumber', j.idNumber);
      m.set('idName', j.idName);
      m.set('createdAt', j.createdAt);
      m.set('updatedAt', j.updatedAt);
      m.set('type', j.type);
      m.set('roles', new List(j.roles));
    });
  }
}

class RoleInfo extends Record({
  roleId: undefined,
  displayName: undefined,
}, 'RoleInfo') {
  static fromJson(j) {

  }
}

class PermissionInfo extends Record({
  permissionId: undefined,
  displayName: undefined,
}, 'PermissionInfo') {
  static fromJson(j) {

  }
}

class AuthState extends Record({
  loaded: false,              // whether auto login has finished
  activeUserId: undefined,    // current login user
  token: undefined,           // current login user token
  users: Map(),            // cached user info list
}, 'AuthState') {

}

// --- constant

const LOADED = 'AUTH@LOADED';
const LOGIN_WITH_MOBILE_PHONE = 'LOGIN_WITH_MOBILE_PHONE';
const AUTO_LOGIN = 'AUTO_LOGIN';
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
const LOGOUT = 'LOGOUT';
const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';

// --- action

export const authAction = {
  loginWithMobilePhone: createAction(LOGIN_WITH_MOBILE_PHONE),
  autoLogin: createAction(AUTO_LOGIN),
  logout: createAction(LOGOUT),
  loginSuccess: createAction(LOGIN_SUCCESS),
};

const loaded = createAction(LOADED);
const logoutSuccess = createAction(LOGOUT_SUCCESS);

// --- saga

function* sagaLoginWithMobilePhone(action) {
  const payload = action.payload;

  try {
    // result user: {
    //   userInfo,
    //   token
    // }
    const user = yield call(authCloud.loginWithMobilePhone, payload);

    const info = UserInfo.fromJson(user.userInfo);

    yield put(authAction.loginSuccess({userInfo: info, token: user.token}));

    if (payload.onSuccess) {
      payload.onSuccess();
    }

    console.log('login succeeded：', info);
  } catch (e) {
    console.log('login failed：', e);
  }
}

function* sagaAutoLogin(action) {
  const payload = action.payload;

  try {
    const user = yield call(authCloud.become, payload);

    const info = UserInfo.fromJson(user.userInfo);

    yield put(authAction.loginSuccess({userInfo: info, token: user.token}));

    console.log('auto login succeeded：', info);
  } catch(e) {
    console.log('auto login failed：', e);
  }

  yield put(loaded({}));
}

function* sagaLogout(action) {
  const payload = action.payload;

  try {
    yield call(authCloud.logout, payload);
  } catch (e) {
  }

  yield put(logoutSuccess({}));

  if (payload.onSuccess) {
    payload.onSuccess();
  }
}

export const authSaga = [
  takeLatest(LOGIN_WITH_MOBILE_PHONE, sagaLoginWithMobilePhone),
  takeLatest(AUTO_LOGIN, sagaAutoLogin),
  takeLatest(LOGOUT, sagaLogout),
];

// --- reducer

const initialState = new AuthState();

export function authReducer(state=initialState, action) {
  switch(action.type) {
    case LOADED:
      return reduceLoaded(state, action);
    case LOGIN_SUCCESS:
      return reduceLoginSuccess(state, action);
    case LOGOUT_SUCCESS:
      return reduceLogoutSuccess(state, action);
    case REHYDRATE:
      return reduceRehydrate(state, action);
    default:
      return state
  }
}

function reduceLoaded(state, action) {
  return state.set('loaded', true);
}

function reduceLoginSuccess(state, action) {
  const {userInfo, token} = action.payload;

  return state.withMutations((m) => {
    m.set('activeUserId', userInfo.id);
    m.set('token', token);
    m.setIn(['users', userInfo.id], userInfo);
  });
}

function reduceLogoutSuccess(state, action) {
  const activeUserId = state.get('activeUserId');

  return state.withMutations((m) => {
    m.set('activeUserId', undefined);
    m.set('token', undefined);
    m.deleteIn(['users', activeUserId]);
  });
}

function reduceRehydrate(state, action) {
  const incoming = action.payload.AUTH;

  if (!incoming) {
    return state;
  }

  if (!incoming.activeUserId) {
    return state;
  }

  // always re-auth with the server
  // state = state.set('activeUserId', incoming.activeUserId);
  state = state.set('token', incoming.token);

  // convert from plain json dict to immutable.js Map
  const users = Map(incoming.users);
  try {
    for (let [id, profile] of users) {
      if (id && profile) {
        const userInfo = new UserInfo({...profile});
        state = state.setIn(['users', id], userInfo);
      }
    }
  } catch (e) {
    users.clear();
  }

  return state;
}

// --- selector

function selectLoaded(appState) {
  return appState.AUTH.loaded;
}

function selectActiveUserId(appState) {
  return appState.AUTH.activeUserId;
}

function selectActiveUserIdAndToken(appState) {
  return {
    activeUserId: appState.AUTH.activeUserId,
    token: appState.AUTH.token
  };
}

function selectIsUserLoggedIn(appState) {
  const activeUserId = selectActiveUserIdAndToken(appState).activeUserId;
  return activeUserId !== undefined;
}

function selectUserInfoById(appState, id) {
  return appState.AUTH.getIn(['users', id], undefined);
}

function selectUserInfoByIds(appState, ids) {
  let infos = [];

  ids.forEach((id) => {
    const info = selectUserInfoById(appState, id);

    // TODO: invalid user id ?
    if (info !== undefined) {
      infos.push(info.toJS());
    }
  });

  return infos;
}

function selectActiveUserInfo(appState) {
  const activeUserId = selectActiveUserId(appState);

  return activeUserId !== undefined ? appState.AUTH.selectUserInfoById(activeUserId) : undefined;
}

function selectToken(appState) {
  let AUTH = appState.AUTH;
  return AUTH.token;
}

export const authSelector = {
  selectLoaded,
  selectActiveUserId,
  selectIsUserLoggedIn,
  selectUserInfoById,
  selectUserInfoByIds,
  selectActiveUserInfo,
  selectToken,
};
<|endoftext|>


<commit_before>// Contains utils to download the locale data for the current language, eventually
// requiring the `Intl` polyfill for browser not supporting it
// It is used in client.js *before* rendering the root component.

const debug = require("debug")("isomorphic500");

const IntlUtils = {

  hasBuiltInLocaleData(locale) {
    return Intl.NumberFormat.supportedLocalesOf(locale)[0] === locale &&
      Intl.DateTimeFormat.supportedLocalesOf(locale)[0] === locale;
  },

  // Returns a promise which is resolved when Intl chunk has been
  // downloaded and required
  loadIntlPolyfill(locale) {

    return new Promise((resolve) => {

      if (window.Intl && IntlUtils.hasBuiltInLocaleData(locale)) {

        // all fine: Intl is in the global scope and the locale data is available
        return resolve();
      }

      debug("Intl or locale data for %s not available, downloading the polyfill...", locale);

      // Create a intl chunk with webpack and run the callback once it has
      // been download

      require.ensure(["intl"], (require) => {
        const IntlPolyfill = require("intl");

        Intl.NumberFormat = IntlPolyfill.NumberFormat;
        Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;

        debug("Intl polyfill for %s has been loaded", locale);

        resolve();
      }, "intl");

    });

  },

  // Returns a promise which is resolved as the required locale-data chunks
  // has been downloaded with webpack's require.ensure. For each language,
  // we make two different chunks: one for browsers supporting `intl` and one
  // for those who don't.
  // The react-intl locale-data is required, for example, by the FormattedRelative
  // component.
  loadLocaleData(locale) {
    const hasIntl = IntlUtils.hasBuiltInLocaleData(locale);

    // Make sure ReactIntl is in the global scope: this is required for adding locale-data
    // Since ReactIntl needs the `Intl` polyfill to be required (sic) we must place
    // this require here, when loadIntlPolyfill is supposed to be present
    require("expose?ReactIntl!react-intl");

    return new Promise((resolve) => {

      switch (locale) {

        // italian
        case "it":

          if (!hasIntl) {

            require.ensure([
                "intl/locale-data/jsonp/it",
                "react-intl/dist/locale-data/it"
              ], (require) => {
              require("intl/locale-data/jsonp/it");
              require("react-intl/dist/locale-data/it");
              debug("Intl and ReactIntl locale-data for %s has been downloaded", locale);
              resolve();
            }, "locale-it");
          }
          else {
            require.ensure([
                "react-intl/dist/locale-data/it"
              ], (require) => {
              require("react-intl/dist/locale-data/it");
              debug("ReactIntl locale-data for %s has been downloaded", locale);
              resolve();
            }, "locale-it-no-intl");
          }

        break;

        // english
        default:
          if (!hasIntl) {
            require.ensure([
                "intl/locale-data/jsonp/en"
              ], (require) => {

              require("intl/locale-data/jsonp/en");
              debug("Intl locale-data for %s has been downloaded", locale);
              resolve();
            }, "locale-en");
          }
          else {
            resolve();
          }

      }

    });

  }

};

export default IntlUtils;
<commit_msg>Require intl/intl instead of the whole intl (fix #50)<commit_after>// Contains utils to download the locale data for the current language, eventually
// requiring the `Intl` polyfill for browser not supporting it
// It is used in client.js *before* rendering the root component.

const debug = require("debug")("isomorphic500");

const IntlUtils = {

  hasBuiltInLocaleData(locale) {
    return Intl.NumberFormat.supportedLocalesOf(locale)[0] === locale &&
      Intl.DateTimeFormat.supportedLocalesOf(locale)[0] === locale;
  },

  // Returns a promise which is resolved when Intl chunk has been
  // downloaded and required
  loadIntlPolyfill(locale) {

    return new Promise((resolve) => {

      if (window.Intl && IntlUtils.hasBuiltInLocaleData(locale)) {

        // all fine: Intl is in the global scope and the locale data is available
        return resolve();
      }

      debug("Intl or locale data for %s not available, downloading the polyfill...", locale);

      // Create a intl chunk with webpack and run the callback once it has
      // been download. Make sure to require `intl/intl` and not just `intl`
      // otherwise webpack will require the *complete* intl package (with all
      // the locales)

      require.ensure(["intl/intl"], (require) => {
        const IntlPolyfill = require("intl/intl");

        Intl.NumberFormat = IntlPolyfill.NumberFormat;
        Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;

        debug("Intl polyfill for %s has been loaded", locale);

        resolve();
      }, "intl");

    });

  },

  // Returns a promise which is resolved as the required locale-data chunks
  // has been downloaded with webpack's require.ensure. For each language,
  // we make two different chunks: one for browsers supporting `intl` and one
  // for those who don't.
  // The react-intl locale-data is required, for example, by the FormattedRelative
  // component.
  loadLocaleData(locale) {
    const hasIntl = IntlUtils.hasBuiltInLocaleData(locale);

    // Make sure ReactIntl is in the global scope: this is required for adding locale-data
    // Since ReactIntl needs the `Intl` polyfill to be required (sic) we must place
    // this require here, when loadIntlPolyfill is supposed to be present
    require("expose?ReactIntl!react-intl");

    return new Promise((resolve) => {

      switch (locale) {

        // italian
        case "it":

          if (!hasIntl) {

            require.ensure([
                "intl/locale-data/jsonp/it",
                "react-intl/dist/locale-data/it"
              ], (require) => {
              require("intl/locale-data/jsonp/it");
              require("react-intl/dist/locale-data/it");
              debug("Intl and ReactIntl locale-data for %s has been downloaded", locale);
              resolve();
            }, "locale-it");
          }
          else {
            require.ensure([
                "react-intl/dist/locale-data/it"
              ], (require) => {
              require("react-intl/dist/locale-data/it");
              debug("ReactIntl locale-data for %s has been downloaded", locale);
              resolve();
            }, "locale-it-no-intl");
          }

        break;

        // english
        default:
          if (!hasIntl) {
            require.ensure([
                "intl/locale-data/jsonp/en"
              ], (require) => {

              require("intl/locale-data/jsonp/en");
              debug("Intl locale-data for %s has been downloaded", locale);
              resolve();
            }, "locale-en");
          }
          else {
            resolve();
          }

      }

    });

  }

};

export default IntlUtils;
<|endoftext|>


<commit_before>import * as t from 'babel-types'

const importLocalName = (name, state) => {
  const imports = state.file.metadata.modules.imports
  const styledImports = imports.find(x => x.source === 'styled-components')
  if (styledImports) {
    const specifier = styledImports.specifiers.find(x => x.imported === name)
    if (specifier) {
      return specifier.local
    }
  }
  // import not found - return default name
  return name === 'default' ? 'styled' : name
}

export const isStyled = (tag, state) => {
  if (t.isCallExpression(tag) && t.isMemberExpression(tag.callee) && tag.callee.property.name !== 'default') {
    // styled.something()
    return isStyled(tag.callee.object, state)
  } else {
    return (
      (t.isMemberExpression(tag) && tag.object.name === importLocalName('default', state)) ||
      (t.isCallExpression(tag) && tag.callee.name === importLocalName('default', state)) ||
      (state.styledRequired && t.isMemberExpression(tag) && t.isMemberExpression(tag.object) && tag.object.property.name === 'default' && tag.object.object.name === state.styledRequired) ||
      (state.styledRequired && t.isCallExpression(tag) && t.isMemberExpression(tag.callee) && tag.callee.property.name === 'default' && tag.callee.object.name === state.styledRequired)
    )
  }
}

export const isCSSHelper = (tag, state) => (
  t.isIdentifier(tag) &&
  tag.name === importLocalName('css', state)
)

export const isInjectGlobalHelper = (tag, state) => (
  t.isIdentifier(tag) &&
  tag.name === importLocalName('injectGlobal', state)
)

export const isKeyframesHelper = (tag, state) => (
  t.isIdentifier(tag) &&
  tag.name === importLocalName('keyframes', state)
)

export const isHelper = (tag, state) => (
  isCSSHelper(tag, state) ||
  isKeyframesHelper(tag, state)
)
<commit_msg>add comment for specific detector<commit_after>import * as t from 'babel-types'

const importLocalName = (name, state) => {
  const imports = state.file.metadata.modules.imports
  const styledImports = imports.find(x => x.source === 'styled-components')
  if (styledImports) {
    const specifier = styledImports.specifiers.find(x => x.imported === name)
    if (specifier) {
      return specifier.local
    }
  }
  // import not found - return default name
  return name === 'default' ? 'styled' : name
}

export const isStyled = (tag, state) => {
  if (t.isCallExpression(tag) && t.isMemberExpression(tag.callee) && tag.callee.property.name !== 'default' /** ignore default for #93 below */) {
    // styled.something()
    return isStyled(tag.callee.object, state)
  } else {
    return (
      (t.isMemberExpression(tag) && tag.object.name === importLocalName('default', state)) ||
      (t.isCallExpression(tag) && tag.callee.name === importLocalName('default', state)) ||

      /**
       * Support Typescript import #93
       * Typescript transpile output come with member expression `default`
       * ex.
       * ------
       * // Component.ts (before)
       *  import styled from 'styled-component';
       *  const Input = styled.input`width:100%;`
       * ------
       * // Component.js (transpiled)
       *  const styled_components_1 = require('styled-component');
       *  const Input = styled.default.input`width:100%;`
       */
      (state.styledRequired && t.isMemberExpression(tag) && t.isMemberExpression(tag.object) && tag.object.property.name === 'default' && tag.object.object.name === state.styledRequired) ||
      (state.styledRequired && t.isCallExpression(tag) && t.isMemberExpression(tag.callee) && tag.callee.property.name === 'default' && tag.callee.object.name === state.styledRequired)
    )
  }
}

export const isCSSHelper = (tag, state) => (
  t.isIdentifier(tag) &&
  tag.name === importLocalName('css', state)
)

export const isInjectGlobalHelper = (tag, state) => (
  t.isIdentifier(tag) &&
  tag.name === importLocalName('injectGlobal', state)
)

export const isKeyframesHelper = (tag, state) => (
  t.isIdentifier(tag) &&
  tag.name === importLocalName('keyframes', state)
)

export const isHelper = (tag, state) => (
  isCSSHelper(tag, state) ||
  isKeyframesHelper(tag, state)
)
<|endoftext|>


<commit_before>const DEBUG = false;
const map = new Map;

export function timeStart ( label ) {
	if ( !map.has( label ) ) {
		map.set( label, {
			time: 0
		});
	}
	map.get( label ).start = process.hrtime();
}

export function timeEnd ( label ) {
	if ( map.has( label ) ) {
		const item = map.get( label );
		item.time += toMilliseconds( process.hrtime( item.start ) );
	}
}

export function flushTime ( log = defaultLog ) {
	for ( const item of map.entries() ) {
		log( item[0], item[1].time );
	}
	map.clear();
}

function toMilliseconds ( time ) {
	return time[0] * 1e+3 + Math.floor( time[1] * 1e-6 );
}

function defaultLog ( label, time ) {
	if ( DEBUG ) {
		/* eslint-disable no-console */
		console.info( '%dms: %s', time, label );
		/* eslint-enable no-console */
	}
}
<commit_msg>browser friendly flushTime<commit_after>const DEBUG = false;
const map = new Map;

let time;

if ( typeof process === 'undefined' ) {
	time = function time ( previous ) {
		const now = window.performance.now();
		return previous ? previous - now : now;
	};
} else {
	time = function time ( previous ) {
		const hrtime = process.hrtime( previous );
		if ( previous ) {
			return hrtime[0] * 1e3 + hrtime[1] / 1e6;
		}
	};
}

export function timeStart ( label ) {
	if ( !map.has( label ) ) {
		map.set( label, {
			time: 0
		});
	}
	map.get( label ).start = time();
}

export function timeEnd ( label ) {
	if ( map.has( label ) ) {
		const item = map.get( label );
		item.time += time( item.start );
	}
}

export function flushTime ( log = defaultLog ) {
	for ( const item of map.entries() ) {
		log( item[0], item[1].time );
	}
	map.clear();
}

function defaultLog ( label, time ) {
	if ( DEBUG ) {
		/* eslint-disable no-console */
		console.info( '%dms: %s', time, label );
		/* eslint-enable no-console */
	}
}
<|endoftext|>


<commit_before>'use strict';

const exec = require('cp-sugar').exec;
const pathJoin = require('path').join;
const readFile = require('fs').readFileSync;
const unlink = require('fs').unlinkSync;
const sep = require('path').sep;
const tempFolder = require('osenv').tmpdir();
const path = require('path');
const EOL = require('os').EOL;

// npm audit error codes
/**
 * Missing package-lock.json
 */
const EAUDITNOLOCK = 'EAUDITNOLOCK';

/**
 * Audit the project in directory projectDir
 * @param {(string | undefined)} projectDir - project directory to be analyzed by npm audit
 */
module.exports = function audit(projectDir) {
    try {
        const options = getDefaultOptionsFor(projectDir);
        process.chdir(options.directoryToAudit);
        const command = `npm audit --json > ${options.auditLogFilepath}`;

        return exec(command)
            .then(() => createResponseFromAuditLog(options.auditLogFilepath))
            .catch((err) =>
                createResponseFromAuditLogOrFromError(
                    options.auditLogFilepath,
                    err
                )
            )
            .then((response) => {
                if (packageLockHasBeenFound(response)) {
                    return removeIgnoredVulnerabilities(response, options);
                }

                const createLockFileCommand = `npm i --package-lock-only > ${
                    options.createLockLogFilepath
                }`;

                return exec(createLockFileCommand)
                    .then(() => exec(command))
                    .then(() =>
                        createResponseFromAuditLog(options.auditLogFilepath)
                    )
                    .catch((err) =>
                        createResponseFromAuditLogOrFromError(
                            options.auditLogFilepath,
                            err
                        )
                    )
                    .then((result) =>
                        processResult(result).whenErrorIs(EAUDITNOLOCK)
                    )
                    .then((result) =>
                        removePackageLockFrom(options.directoryToAudit, result)
                    )
                    .then((result) =>
                        removeIgnoredVulnerabilities(result, options)
                    );
            });
    } catch (error) {
        return Promise.reject(error.message);
    }
};

function packageLockHasNotBeenFound(response) {
    return response && response.error && response.error.code === EAUDITNOLOCK;
}

function packageLockHasBeenFound(response) {
    return !packageLockHasNotBeenFound(response);
}

function createResponseFromAuditLog(logFilePath) {
    try {
        const response = JSON.parse(readFile(logFilePath).toString());
        return response;
    } catch (err) {
        return {
            error: {
                summary: err.message,
            },
        };
    }
}

function createResponseFromAuditLogOrFromError(logFilePath, err) {
    try {
        const response = JSON.parse(readFile(logFilePath).toString());
        return response;
    } catch (err2) {
        // prettier-ignore
        return {
            error: {
                summary: err && err.message
                    ? err.message
                    : err2.message,
            },
        };
    }
}

/**
 * Middleware that intercept any input error object.
 * This middleware may modify the inital error message
 * @param {Object} result -  result of the npm audit command
 */
function processResult(result) {
    return {
        whenErrorIs: (errorCode) => {
            if (
                errorCode === EAUDITNOLOCK &&
                packageLockHasNotBeenFound(result)
            ) {
                const summary = result.error.summary || '';
                result.error.summary = `package.json file is missing or is badly formatted. ${summary}`;
                return result;
            }
            return result;
        },
    };
}

/**
 * Middleware that removes the auto-generated package-lock.json
 * @param {string} projectDir - folder where the package-lock.json file has been generated
 * @param {*} response - result of the npm audit command (eventually modified by previous middlewares execution)
 * @returns input response if file removal is ok
 * In case of error it adds or updates into the input response object the property 'internalErrors'
 */
function removePackageLockFrom(projectDir, response) {
    try {
        const file = pathJoin(projectDir, 'package-lock.json');
        unlink(file);
        return response;
    } catch (error) {
        if (error.code === 'ENOENT') {
            return response;
        }
        if (response) {
            response.internalErrors = response.internalErrors || [];
            response.internalErrors.push(error);
            return response;
        }
        return response;
    }
}

/**
 * exported for testing purposes
 */
module.exports.removePackageLockFrom = removePackageLockFrom;

/**
 * Remove, from npm audit command response, the vulnerabilities defined in .auditignore file
 * @param {*} response - result of the npm audit command (eventually modified by previous middlewares execution)
 * @param {DefaultOptions} options - options
 */
function removeIgnoredVulnerabilities(response, options) {
    try {
        const ignoredVulnerabilities = getIgnoredVulnerabilities(options);
        console.log('ignored vulnerabilities found:');
        console.log(ignoredVulnerabilities);
        console.log('');
        console.log('');
        if (ignoredVulnerabilities && ignoredVulnerabilities.length === 0) {
            return response;
        }
        const filteredResponse = JSON.parse(JSON.stringify(response, null, 2));

        filteredResponse.actions = filteredResponse.actions
            .map((action) => {
                action.resolves = action.resolves.filter(
                    (resolve) =>
                        ignoredVulnerabilities.indexOf(`${resolve.id}`) < 0
                );
                return action;
            })
            .filter((action) => action.resolves.length > 0);

        ignoredVulnerabilities.forEach(
            (ignoredVulnerability) =>
                delete filteredResponse.advisories[ignoredVulnerability]
        );

        const vulnerabilitiesMetadata = {
            info: 0,
            low: 0,
            moderate: 0,
            high: 0,
            critical: 0,
        };

        const severities = {};
        const advisories = filteredResponse.advisories;
        for (const key in advisories) {
            if (advisories.hasOwnProperty(key)) {
                const advisory = advisories[key];
                severities[`${advisory.id}`] = advisory.severity;
            }
        }

        filteredResponse.actions.forEach((action) => {
            action.resolves.forEach((resolve) => {
                vulnerabilitiesMetadata[severities[`${resolve.id}`]] += 1;
            });
        });

        filteredResponse.metadata.vulnerabilities = vulnerabilitiesMetadata;

        return filteredResponse;
    } catch (error) {
        if (response) {
            response.internalErrors = response.internalErrors || [];
            response.internalErrors.push(error);
            return response;
        }
        return response;
    }
}

/**
 * exported for testing purposes
 */
module.exports.removeIgnoredVulnerabilities = removeIgnoredVulnerabilities;

/**
 * @typedef DefaultOptions
 * @type {Object}
 * @property {string} directoryToAudit - Folder to audit. Defaults to process.cwd()
 * @property {string} auditLogFilepath - Path of the file that will receive all output of the npm-audit command.
 * @property {string} createLockLogFilepath - Path of the file that will receive all output of the 'npm i --package-lock-only' command.
 */

/**
 * Get default options of this module
 * @param {string} projectDir - path to the directory that will be analyzed by npm audit
 * @returns {DefaultOptions}
 */
function getDefaultOptionsFor(projectDir) {
    const directoryToAudit = projectDir || process.cwd();
    const projectName = directoryToAudit.split(sep).pop() || '';
    const auditLogFilename = `npm-audit-${projectName.trim()}.log`;
    const auditLogFilepath = path.resolve(tempFolder, auditLogFilename);
    const createLockLogFilename = `npm-audit-${projectName.trim()}-create-lock.log`;
    const createLockLogFilepath = path.resolve(
        tempFolder,
        createLockLogFilename
    );

    return {
        directoryToAudit,
        auditLogFilepath,
        createLockLogFilepath,
    };
}

function getIgnoredVulnerabilities(options) {
    try {
        const auditIgnoreFile = pathJoin(
            options.directoryToAudit,
            '.auditignore'
        );
        const content = readFile(auditIgnoreFile).toString();
        console.log('found .auditignore file with content:');
        console.log(content);
        return content
            .split(EOL)
            .filter((vulnerabilityUri) => {
                console.log(`vulnerabilityUri: ${vulnerabilityUri}`);
                return vulnerabilityUri.includes('https');
            })
            .map((vulnerabilityUri) => vulnerabilityUri.split('/').pop());
    } catch (error) {
        console.log(error.message);
        return [];
    }
}
<commit_msg>test(npm): try fix test on windows CI<commit_after>'use strict';

const exec = require('cp-sugar').exec;
const pathJoin = require('path').join;
const readFile = require('fs').readFileSync;
const unlink = require('fs').unlinkSync;
const sep = require('path').sep;
const tempFolder = require('osenv').tmpdir();
const path = require('path');
const EOL = require('os').EOL;

// npm audit error codes
/**
 * Missing package-lock.json
 */
const EAUDITNOLOCK = 'EAUDITNOLOCK';

/**
 * Audit the project in directory projectDir
 * @param {(string | undefined)} projectDir - project directory to be analyzed by npm audit
 */
module.exports = function audit(projectDir) {
    try {
        const options = getDefaultOptionsFor(projectDir);
        process.chdir(options.directoryToAudit);
        const command = `npm audit --json > ${options.auditLogFilepath}`;

        return exec(command)
            .then(() => createResponseFromAuditLog(options.auditLogFilepath))
            .catch((err) =>
                createResponseFromAuditLogOrFromError(
                    options.auditLogFilepath,
                    err
                )
            )
            .then((response) => {
                if (packageLockHasBeenFound(response)) {
                    return removeIgnoredVulnerabilities(response, options);
                }

                const createLockFileCommand = `npm i --package-lock-only > ${
                    options.createLockLogFilepath
                }`;

                return exec(createLockFileCommand)
                    .then(() => exec(command))
                    .then(() =>
                        createResponseFromAuditLog(options.auditLogFilepath)
                    )
                    .catch((err) =>
                        createResponseFromAuditLogOrFromError(
                            options.auditLogFilepath,
                            err
                        )
                    )
                    .then((result) =>
                        processResult(result).whenErrorIs(EAUDITNOLOCK)
                    )
                    .then((result) =>
                        removePackageLockFrom(options.directoryToAudit, result)
                    )
                    .then((result) =>
                        removeIgnoredVulnerabilities(result, options)
                    );
            });
    } catch (error) {
        return Promise.reject(error.message);
    }
};

function packageLockHasNotBeenFound(response) {
    return response && response.error && response.error.code === EAUDITNOLOCK;
}

function packageLockHasBeenFound(response) {
    return !packageLockHasNotBeenFound(response);
}

function createResponseFromAuditLog(logFilePath) {
    try {
        const response = JSON.parse(readFile(logFilePath).toString());
        return response;
    } catch (err) {
        return {
            error: {
                summary: err.message,
            },
        };
    }
}

function createResponseFromAuditLogOrFromError(logFilePath, err) {
    try {
        const response = JSON.parse(readFile(logFilePath).toString());
        return response;
    } catch (err2) {
        // prettier-ignore
        return {
            error: {
                summary: err && err.message
                    ? err.message
                    : err2.message,
            },
        };
    }
}

/**
 * Middleware that intercept any input error object.
 * This middleware may modify the inital error message
 * @param {Object} result -  result of the npm audit command
 */
function processResult(result) {
    return {
        whenErrorIs: (errorCode) => {
            if (
                errorCode === EAUDITNOLOCK &&
                packageLockHasNotBeenFound(result)
            ) {
                const summary = result.error.summary || '';
                result.error.summary = `package.json file is missing or is badly formatted. ${summary}`;
                return result;
            }
            return result;
        },
    };
}

/**
 * Middleware that removes the auto-generated package-lock.json
 * @param {string} projectDir - folder where the package-lock.json file has been generated
 * @param {*} response - result of the npm audit command (eventually modified by previous middlewares execution)
 * @returns input response if file removal is ok
 * In case of error it adds or updates into the input response object the property 'internalErrors'
 */
function removePackageLockFrom(projectDir, response) {
    try {
        const file = pathJoin(projectDir, 'package-lock.json');
        unlink(file);
        return response;
    } catch (error) {
        if (error.code === 'ENOENT') {
            return response;
        }
        if (response) {
            response.internalErrors = response.internalErrors || [];
            response.internalErrors.push(error);
            return response;
        }
        return response;
    }
}

/**
 * exported for testing purposes
 */
module.exports.removePackageLockFrom = removePackageLockFrom;

/**
 * Remove, from npm audit command response, the vulnerabilities defined in .auditignore file
 * @param {*} response - result of the npm audit command (eventually modified by previous middlewares execution)
 * @param {DefaultOptions} options - options
 */
function removeIgnoredVulnerabilities(response, options) {
    try {
        const ignoredVulnerabilities = getIgnoredVulnerabilities(options);
        console.log('ignored vulnerabilities found:');
        console.log(ignoredVulnerabilities);
        console.log('');
        console.log('');
        if (ignoredVulnerabilities && ignoredVulnerabilities.length === 0) {
            return response;
        }
        const filteredResponse = JSON.parse(JSON.stringify(response, null, 2));

        filteredResponse.actions = filteredResponse.actions
            .map((action) => {
                action.resolves = action.resolves.filter(
                    (resolve) =>
                        ignoredVulnerabilities.indexOf(`${resolve.id}`) < 0
                );
                return action;
            })
            .filter((action) => action.resolves.length > 0);

        ignoredVulnerabilities.forEach(
            (ignoredVulnerability) =>
                delete filteredResponse.advisories[ignoredVulnerability]
        );

        const vulnerabilitiesMetadata = {
            info: 0,
            low: 0,
            moderate: 0,
            high: 0,
            critical: 0,
        };

        const severities = {};
        const advisories = filteredResponse.advisories;
        for (const key in advisories) {
            if (advisories.hasOwnProperty(key)) {
                const advisory = advisories[key];
                severities[`${advisory.id}`] = advisory.severity;
            }
        }

        filteredResponse.actions.forEach((action) => {
            action.resolves.forEach((resolve) => {
                vulnerabilitiesMetadata[severities[`${resolve.id}`]] += 1;
            });
        });

        filteredResponse.metadata.vulnerabilities = vulnerabilitiesMetadata;

        return filteredResponse;
    } catch (error) {
        if (response) {
            response.internalErrors = response.internalErrors || [];
            response.internalErrors.push(error);
            return response;
        }
        return response;
    }
}

/**
 * exported for testing purposes
 */
module.exports.removeIgnoredVulnerabilities = removeIgnoredVulnerabilities;

/**
 * @typedef DefaultOptions
 * @type {Object}
 * @property {string} directoryToAudit - Folder to audit. Defaults to process.cwd()
 * @property {string} auditLogFilepath - Path of the file that will receive all output of the npm-audit command.
 * @property {string} createLockLogFilepath - Path of the file that will receive all output of the 'npm i --package-lock-only' command.
 */

/**
 * Get default options of this module
 * @param {string} projectDir - path to the directory that will be analyzed by npm audit
 * @returns {DefaultOptions}
 */
function getDefaultOptionsFor(projectDir) {
    const directoryToAudit = projectDir || process.cwd();
    const projectName = directoryToAudit.split(sep).pop() || '';
    const auditLogFilename = `npm-audit-${projectName.trim()}.log`;
    const auditLogFilepath = path.resolve(tempFolder, auditLogFilename);
    const createLockLogFilename = `npm-audit-${projectName.trim()}-create-lock.log`;
    const createLockLogFilepath = path.resolve(
        tempFolder,
        createLockLogFilename
    );

    return {
        directoryToAudit,
        auditLogFilepath,
        createLockLogFilepath,
    };
}

function getIgnoredVulnerabilities(options) {
    try {
        const auditIgnoreFile = pathJoin(
            options.directoryToAudit,
            '.auditignore'
        );
        const content = readFile(auditIgnoreFile).toString();
        return content
            .split(EOL)
            .filter((vulnerabilityUri) => vulnerabilityUri.includes('https'))
            .map((vulnerabilityUri) => vulnerabilityUri.split('/').pop())
            .map((id) => id.replace('\n', ''))
            .map((id) => id.replace('\r', ''))
            .map((id) => id.trim());
    } catch (error) {
        return [];
    }
}
<|endoftext|>


<commit_before>const _ = require('underscore')

let ENABLE_LOGGING = true;
const LOGGER_COLORS = [
  '#E91E63',
  '#9C27B0',
  '#673AB7',
  '#3F51B5',
  '#2196F3',
  '#009688',
  '#4CAF50',
  '#FF5722',
  '#795548',
  '#607D8B',
]
const colorsByPrefix = {}
let curColor = 0

function getColorForPrefix(prefix) {
  if (colorsByPrefix[prefix]) {
    return colorsByPrefix[prefix]
  }
  colorsByPrefix[prefix] = LOGGER_COLORS[curColor]
  curColor = (curColor + 1) % LOGGER_COLORS.length
  return colorsByPrefix[prefix]
}

function Logger(boundArgs = {}) {
  if (NylasEnv && !NylasEnv.inDevMode()) {
    ENABLE_LOGGING = false
  }

  if (!_.isObject(boundArgs)) {
    throw new Error('Logger: Bound arguments must be an object')
  }
  const logger = {}
  const loggerFns = ['log', 'debug', 'info', 'warn', 'error']
  loggerFns.forEach((logFn) => {
    logger[logFn] = (...args) => {
      if (!ENABLE_LOGGING && logFn !== "error") {
        return
      }
      const {accountId, accountEmail, ...otherArgs} = boundArgs
      const prefix = accountEmail || accountId
      const suffix = !_.isEmpty(otherArgs) ? otherArgs : '';
      let [first, ...extraArgs] = args
      if (_.isObject(first)) {
        [first, extraArgs] = [extraArgs, [first]]
      }
      if (prefix) {
        const color = getColorForPrefix(prefix)
        console[logFn](
          `%c<${prefix}> %c${first}`,
          `color: ${color}`,
          `color: #333333`,
          ...extraArgs,
          suffix
        )
        return
      }
      console[logFn](`${first}`, ...extraArgs, suffix)
    }
  })
  logger.boundArgs = boundArgs
  logger.child = (extraBoundArgs) => Logger({...boundArgs, ...extraBoundArgs})
  return logger
}

function createLogger() {
  const childLogs = new Map()
  const logger = Logger()

  return Object.assign(logger, {
    forAccount(account = {}) {
      if (!childLogs.has(account.id)) {
        const childLog = logger.child({
          accountId: account.id,
          accountEmail: account.emailAddress,
        })
        childLogs.set(account.id, childLog)
      }
      return childLogs.get(account.id)
    },
  })
}

module.exports = {createLogger}
<commit_msg>[client-sync] Enable logging in prod builds<commit_after>const _ = require('underscore')

const LOGGER_COLORS = [
  '#E91E63',
  '#9C27B0',
  '#673AB7',
  '#3F51B5',
  '#2196F3',
  '#009688',
  '#4CAF50',
  '#FF5722',
  '#795548',
  '#607D8B',
]
const colorsByPrefix = {}
let curColor = 0

function getColorForPrefix(prefix) {
  if (colorsByPrefix[prefix]) {
    return colorsByPrefix[prefix]
  }
  colorsByPrefix[prefix] = LOGGER_COLORS[curColor]
  curColor = (curColor + 1) % LOGGER_COLORS.length
  return colorsByPrefix[prefix]
}

function Logger(boundArgs = {}) {
  if (!_.isObject(boundArgs)) {
    throw new Error('Logger: Bound arguments must be an object')
  }
  const logger = {}
  const loggerFns = ['log', 'debug', 'info', 'warn', 'error']
  loggerFns.forEach((logFn) => {
    logger[logFn] = (...args) => {
      const {accountId, accountEmail, ...otherArgs} = boundArgs
      const prefix = accountEmail || accountId
      const suffix = !_.isEmpty(otherArgs) ? otherArgs : '';
      let [first, ...extraArgs] = args
      if (_.isObject(first)) {
        [first, extraArgs] = [extraArgs, [first]]
      }
      if (prefix) {
        const color = getColorForPrefix(prefix)
        console[logFn](
          `%c<${prefix}> %c${first}`,
          `color: ${color}`,
          `color: #333333`,
          ...extraArgs,
          suffix
        )
        return
      }
      console[logFn](`${first}`, ...extraArgs, suffix)
    }
  })
  logger.boundArgs = boundArgs
  logger.child = (extraBoundArgs) => Logger({...boundArgs, ...extraBoundArgs})
  return logger
}

function createLogger() {
  const childLogs = new Map()
  const logger = Logger()

  return Object.assign(logger, {
    forAccount(account = {}) {
      if (!childLogs.has(account.id)) {
        const childLog = logger.child({
          accountId: account.id,
          accountEmail: account.emailAddress,
        })
        childLogs.set(account.id, childLog)
      }
      return childLogs.get(account.id)
    },
  })
}

module.exports = {createLogger}
<|endoftext|>