<commit_before>function Record(){
    var initOptions = {};
    var self = this;
    var socket=null;
    this.blobs = [];
    self.defaults = {
        outputFileName:"out.mp4",
        streamId : "DEFALUT_STREAM_"+new Date().getTime(),
        saveType : "REMOTE_SAVE",//REMOTE_EMIT,LOCAL_EMIT,SAVE_EMIT
        timeSlice : 500, //500 millisecond blobs,
        hlsPath : "",//out.m3u8
        width : 500,
        height: 500,
        partner:"allytech"
    }
    this.waitingForBlob=false;
    this.recorder;
    if(window.screen.width < 768){
        self.defaults.width = 300;
        self.defaults.height=300;
    }
    this.init = function(options){
        self.socket = options.socket;
        // socketConnect(options.socketServer || self.defaults['socketDomain'],options.signalServer || self.defaults['signalDomain'])
        setup(options);
    }
    var setup = function(options,callback){
        self.saveType = options.saveType || self.defaults.saveType;
        self.options = options;
        self.partner = options.partner || self.defaults.partner;
        self.outputFileName = self.options.outputFileName || self.defaults.outputFileName;
        self.originalOutputFileName = self.options.originalOutputFileName || "original_"+self.outputFileName;
        self.streamId = self.options.streamId || self.defaults.streamId;
        self.socket.on('moreData', requestedMoreData);
        self.fileRoot = "src/public/data/"+self.partner+"/";
        self.accessToken = "";
        self.postId="";
        setupRecording(callback);
    }

    function getParameterByName(name, url) {
      if (!url) {
        url = window.location.href;
      }
      name = name.replace(/[\[\]]/g, '\\$&');
      var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
        results = regex.exec(url);
      if (!results) return null;
      if (!results[2]) return '';
      return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }
    var setupRecording = function(callback){
        startRecordingNow(1);
    }

    function startRecordingNow(startInDuration){
        startRecording();

    }
    function startRecording() {
        uploadStart();
        window.globalRecordingDimension = {"w":(self.options.width || self.defaults.width),"h":(self.options.height || self.defaults.height),"audioOnly":(self.options.audioOnly || false)};
        window.recorder = RecordRTC([self.options.initStream], {
            mimeType: 'video/webm\;codecs=h264',
            timeSlice : self.options.timeSlice || self.defaults.timeSlice,
            type : "video",
            onTimeStamp : function(a,b){
                var player = {};
                player.allTimestamps = b;
                player.currentTimestamp = a;
                var internal = window.recorder.getInternalRecorder().mediaRecorder;
                var blobs = internal.getArrayOfBlobs();
                player.recordedData = internal.getArrayOfBlobs();
                if(player.recordedData) {
                    saveBlobForTimestamp(player);
                }
                if(self.endTriggered){
                  var cTimeStamp = e.currentTimestamp;
                    if(cTimeStamp>endTriggeredTimeStamp){
                        if(self.recorder){
                          self.recorder.stopRecording();
                        }
                        var timeoutId = setInterval(function(){
                            if(!self.blobs.length) {
                                //all blobs transmitted, saved, you can close it now
                                uploadComplete();
                                setTimeout(function(){
                                    if(self.options.onRecordComplete){
                                        self.options.onRecordComplete();
                                    }
                                },1000);
                                clearInterval(timeoutId);
                            }
                        },1000);
                    }
                }

             },
            previewStream: function(s) {
                if(self.options.previewElementId){
                    $("#"+self.options.previewElementId)[0].muted = true;
                    $("#"+self.options.previewElementId)[0].src = URL.createObjectURL(s);
                }

            }
        });
        self.recorder = window.recorder;
        self.recorder.startRecording();
    }
    function endRecording() {
      self.endTriggered=true;
    }
    function uploadStart(){
            if (self.socket) {
                self.socket.emit('uploadStart', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"streamId":self.streamId,"accessToken":self.accessToken,"partner":self.partner,"title":"","postId":"","fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        function uploadResumed(){
            if (self.socket) {
                self.socket.emit('uploadResume', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"streamId":self.streamId,"accessToken":self.accessToken,"partner":self.partner,"title":"","postId":"","fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        function uploadBlob(data,timestamp){
            if (self.socket) {
                self.socket.emit('uploadData', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"timestamp":data.timestamp,"data":data.data,"streamId":self.streamId,"fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        function uploadComplete(){
            if (self.socket) {
                self.socket.emit('uploadComplete', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"streamId":self.streamId,"accessToken":self.accessToken,"partner":self.partner,"title":"","postId":"","fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        var isSaving=false;
        function requestedMoreData(data){
            var timestamp = data.timestamp;
            if(self.blobs.length){
                if(isSaving){
                    setTimeout(function(){
                        var b = self.blobs.shift();
                        uploadBlob(b);
                        waitingForBlob=false;
                    },100);
                } else {
                    var b = self.blobs.shift();
                    uploadBlob(b);
                    waitingForBlob=false;
                }

            } else {
                waitingForBlob=true;
            }
        }

        function saveBlobForTimestamp(player){
            isSaving=true;
            var recordedData = player.recordedData;
            var allTimestamps = player.allTimestamps;
            var currentTimestamp = player.currentTimestamp;
            if(recordedData.length){
                var blob = recordedData[recordedData.length-1];
                var bb = new Blob([blob], {type: 'video/mp4'});
                var item = {"data":bb,"timestamp":currentTimestamp};
                self.blobs.push(item);
                self.blobs = self.blobs.sort(function(a,b){
                    return (a['timestamp']-b['timestamp']);
                });
                isSaving=false;
                if(waitingForBlob){
                    requestedMoreData(blobs[0]);
                }
            } else {
                isSaving=false;
            }
        }

    function captureAudioVideoFromElement(elementId,loop,callback){
        var video1 = document.getElementById(elementId);
        video1.onplay = function() {
          // Set the source of one <video> element to be a stream from another.
          var stream = video1.captureStream();
          cb(stream);
        };
        if (typeof video1.loop == 'boolean' || loop)
          {
              video1.loop = true;
          }
          else
          {
              video1.addEventListener('ended', function() {
                  this.currentTime = 0;
                  this.play();
              }, false);
          }
          video1.play();
    }
};

<commit_msg>cahgnes<commit_after>function Record(){
    var initOptions = {};
    var self = this;
    var socket=null;
    this.blobs = [];
    self.defaults = {
        outputFileName:"out.mp4",
        streamId : "DEFALUT_STREAM_"+new Date().getTime(),
        saveType : "REMOTE_SAVE",//REMOTE_EMIT,LOCAL_EMIT,SAVE_EMIT
        timeSlice : 500, //500 millisecond blobs,
        hlsPath : "",//out.m3u8
        width : 500,
        height: 500,
        partner:"allytech"
    }
    this.waitingForBlob=false;
    this.recorder;
    if(window.screen.width < 768){
        self.defaults.width = 300;
        self.defaults.height=300;
    }
    this.init = function(options){
        self.socket = options.socket;
        // socketConnect(options.socketServer || self.defaults['socketDomain'],options.signalServer || self.defaults['signalDomain'])
        setup(options);
    }
    var setup = function(options,callback){
        self.saveType = options.saveType || self.defaults.saveType;
        self.options = options;
        self.partner = options.partner || self.defaults.partner;
        self.outputFileName = self.options.outputFileName || self.defaults.outputFileName;
        self.originalOutputFileName = self.options.originalOutputFileName || "original_"+self.outputFileName;
        self.streamId = self.options.streamId || self.defaults.streamId;
        self.socket.on('moreData', requestedMoreData);
        self.fileRoot = "src/public/data/"+self.partner+"/";
        self.accessToken = "";
        self.postId="";
        setupRecording(callback);
    }

    function getParameterByName(name, url) {
      if (!url) {
        url = window.location.href;
      }
      name = name.replace(/[\[\]]/g, '\\$&');
      var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
        results = regex.exec(url);
      if (!results) return null;
      if (!results[2]) return '';
      return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }
    var setupRecording = function(callback){
        startRecordingNow(1);
    }

    function startRecordingNow(startInDuration){
        startRecording();

    }
    function startRecording() {
        uploadStart();
        window.globalRecordingDimension = {"w":(self.options.width || self.defaults.width),"h":(self.options.height || self.defaults.height),"audioOnly":(self.options.audioOnly || false)};
        window.recorder = RecordRTC([self.options.initStream], {
            mimeType: 'video/webm\;codecs=h264',
            timeSlice : self.options.timeSlice || self.defaults.timeSlice,
            type : "video",
            onTimeStamp : function(a,b){
                var player = {};
                player.allTimestamps = b;
                player.currentTimestamp = a;
                var internal = window.recorder.getInternalRecorder().mediaRecorder;
                var blobs = internal.getArrayOfBlobs();
                player.recordedData = internal.getArrayOfBlobs();
                if(player.recordedData) {
                    saveBlobForTimestamp(player);
                }
                if(self.endTriggered){
                  var cTimeStamp = e.currentTimestamp;
                    if(cTimeStamp>endTriggeredTimeStamp){
                        if(self.recorder){
                          self.recorder.stopRecording();
                        }
                        var timeoutId = setInterval(function(){
                            if(!self.blobs.length) {
                                //all blobs transmitted, saved, you can close it now
                                uploadComplete();
                                setTimeout(function(){
                                    if(self.options.onRecordComplete){
                                        self.options.onRecordComplete();
                                    }
                                },1000);
                                clearInterval(timeoutId);
                            }
                        },1000);
                    }
                }

             },
            previewStream: function(s) {
                if(self.options.previewElementId){
                    $("#"+self.options.previewElementId)[0].muted = true;
                    $("#"+self.options.previewElementId)[0].src = URL.createObjectURL(s);
                }

            }
        });
        self.recorder = window.recorder;
        self.recorder.startRecording();
    }
    function endRecording() {
      self.endTriggered=true;
    }
    function uploadStart(){
            if (self.socket) {
                self.socket.emit('uploadStart', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"streamId":self.streamId,"accessToken":self.accessToken,"partner":self.partner,"title":"","postId":"","fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        function uploadResumed(){
            if (self.socket) {
                self.socket.emit('uploadResume', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"streamId":self.streamId,"accessToken":self.accessToken,"partner":self.partner,"title":"","postId":"","fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        function uploadBlob(data,timestamp){
            if (self.socket) {
                self.socket.emit('uploadData', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"timestamp":data.timestamp,"data":data.data,"streamId":self.streamId,"fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        function uploadComplete(){
            if (self.socket) {
                self.socket.emit('uploadComplete', {"name":self.outputFileName,"streamName":self.originalOutputFileName,"streamId":self.streamId,"accessToken":self.accessToken,"partner":self.partner,"title":"","postId":"","fileRoot":"src/public/data/"+self.partner+"/"});
            }
        }
        var isSaving=false;
        function requestedMoreData(data){
            var timestamp = data.timestamp;
            if(self.blobs.length){
                if(isSaving){
                    setTimeout(function(){
                        var b = self.blobs.shift();
                        uploadBlob(b);
                        waitingForBlob=false;
                    },100);
                } else {
                    var b = self.blobs.shift();
                    uploadBlob(b);
                    waitingForBlob=false;
                }

            } else {
                waitingForBlob=true;
            }
        }

        function saveBlobForTimestamp(player){
            isSaving=true;
            var recordedData = player.recordedData;
            var allTimestamps = player.allTimestamps;
            var currentTimestamp = player.currentTimestamp;
            if(recordedData.length){
                var blob = recordedData[recordedData.length-1];
                var bb = new Blob([blob], {type: 'video/mp4'});
                var item = {"data":bb,"timestamp":currentTimestamp};
                self.blobs.push(item);
                self.blobs = self.blobs.sort(function(a,b){
                    return (a['timestamp']-b['timestamp']);
                });
                isSaving=false;
                if(waitingForBlob){
                    requestedMoreData(self.blobs[0]);
                }
            } else {
                isSaving=false;
            }
        }

    function captureAudioVideoFromElement(elementId,loop,callback){
        var video1 = document.getElementById(elementId);
        video1.onplay = function() {
          // Set the source of one <video> element to be a stream from another.
          var stream = video1.captureStream();
          cb(stream);
        };
        if (typeof video1.loop == 'boolean' || loop)
          {
              video1.loop = true;
          }
          else
          {
              video1.addEventListener('ended', function() {
                  this.currentTime = 0;
                  this.play();
              }, false);
          }
          video1.play();
    }
};

<|endoftext|>


<commit_before>var $ = require('jquery');
var _ = require('lodash');
var ProductStore = require('../product-store');
var Backbone = require('backdash');
var assert = require('chai').assert;
var sinon = require('sinon');

describe('ProductStore', function() {

  describe('internals.initProducts', function() {
    beforeEach(function() {
      this.sinon = sinon.sandbox.create();
      this.products = ProductStore.__get__('products');
      this.user = ProductStore.__get__('user');
      this.FiltersAction = ProductStore.__get__('FiltersAction');
      this.productsTriggerStub = sinon.stub()
      ProductStore.__set__('products', {
        fetch: () => true,
        trigger: this.productsTriggerStub,
        get: sinon.stub()
      });
      ProductStore.__set__('user', {
        fetch: () => true
      });
      this.filtersInitStub = sinon.stub();
      ProductStore.__set__('FiltersAction', {
        init: this.filtersInitStub
      });
    });

    afterEach(function() {
      ProductStore.__set__('products', this.products);
      ProductStore.__set__('user', this.user);
      ProductStore.__set__('FiltersAction', this.FiltersAction);
      this.sinon.restore();
    });

    it('should return a promise', function() {
      return ProductStore.internals.initProducts();
    });

    context('after successful fetch', function() {
      beforeEach(function() {
        this.createSubscriptionStub = this.sinon.stub(ProductStore.internals, 'createSubscription');
      });

      it('initializes filters data', function(done) {
        ProductStore.internals.initProducts(1).then(() => {
          assert.isTrue(this.filtersInitStub.called);
          done();
        })
      });

      it('subscribes to product events', function(done) {
        ProductStore.internals.initProducts(1).then(() => {
          assert.isTrue(this.createSubscriptionStub.called);
          done();
        })
      });

      it('triggers a change event', function(done) {
        ProductStore.internals.initProducts(1).then(() => {
          assert.isTrue(this.productsTriggerStub.called);
          done();
        });
      });

      context('no product id', function() {
        it('triggers a change event', function(done) {
          ProductStore.internals.initProducts(1).then(() => {
            assert.isTrue(this.productsTriggerStub.called);
            done();
          });
        });
      });
    });
  });

  describe('internals.updateItem', function() {
    beforeEach(function() {
      this.products = ProductStore.__get__('products');
      this.product = this.products.add({ id: 1 });
      this.item = this.product.items.add(
        { id: 1, status: 'in-progress' }
      );
      this.item.save = sinon.stub();
    });

    afterEach(function() {
      this.product.items.reset();
    });

    it('unsets the close reason if status is changing', function() {
      this.item.set({ close_reason: 'fixed' });
      ProductStore.internals.updateItem(1, 1, { status: 'current' });
      assert.isUndefined(this.item.get('close_reason'));
    });

    it('calls item.save', function() {
      ProductStore.internals.updateItem(1, 1, { status: 'current' });
      sinon.assert.calledWith(this.item.save, { status: 'current' });
    });
  });

  describe('internals.loadMoreItems', function() {
    beforeEach(function() {
      this.collection = new Backbone.Collection();
      this.collection.config = new Backbone.Model({
        status: 'backlog',
        offset: 0
      });
    });

    it('should return a promise', function() {
      var spy = sinon.spy(this.collection, 'trigger');

      this.collection.fetch = () => {
        return $.Deferred().resolve({length: 0});
      };

      ProductStore.internals.loadMoreItems(this.collection);
      sinon.assert.calledOnce(spy);
    });

    it('should emit a change event and pass an object containing the response item count', function() {
      var spy = sinon.spy(this.collection, 'trigger');

      this.collection.fetch = () => {
        return $.Deferred().resolve({length: 16});
      };

      ProductStore.internals.loadMoreItems(this.collection);
      sinon.assert.calledWith(spy, 'change', {count: 16});
    });
  });

  describe('updateItemPriority', function() {
    beforeEach(function() {
      this.products = ProductStore.__get__('products');
      this.product = this.products.add({ id: 1 });
      this.backlog = this.product.getItemsByStatus('backlog');
      this.resortStub = sinon.stub(this.product.ItemModel.prototype, 'resort');
      this.backlog.reset([
        { number: 1, status: 'backlog', product: { id: 1 }, sort: 1 },
        { number: 2, status: 'backlog', product: { id: 1 }, sort: 2 },
        { number: 3, status: 'backlog', product: { id: 1 }, sort: 3 }
      ]);
    });

    afterEach(function() {
      this.resortStub.restore();
    });

    after(function() {
      this.backlog.reset();
    });

    it('throws when unexpected sort argument encountered', function() {
      assert.throws(() => ProductStore.internals.updateItemPriority(1,1,'sideways'), /Invalid priority direction/);
    });

    context('up', function() {
      it('calls resort with the expected before and after values when both exist', function() {
        ProductStore.internals.updateItemPriority(1, 3, 'up');
        sinon.assert.calledWith(this.resortStub, { before: 2, after: 1 });
      })

      it('call resort with the expected arguments when no "after" item is found', function() {
        ProductStore.internals.updateItemPriority(1, 2, 'up');
        sinon.assert.calledWith(this.resortStub, { after: 1 });
      })
    });

    context('down', function() {
      it('calls resort with the expected before and after values when both "before" and "after" items exist', function() {
        ProductStore.internals.updateItemPriority(1, 1, 'down');
        sinon.assert.calledWith(this.resortStub, { before: 3, after: 2 });
      })

      it('call resort with the expected arguments when no "before" item is found', function() {
        ProductStore.internals.updateItemPriority(1, 2, 'down');
        sinon.assert.calledWith(this.resortStub, { before: 3 });
      })
    });

    context('top', function() {
      it('calls resort with the correct values', function() {
        ProductStore.internals.updateItemPriority(1, 3, 'top');
        sinon.assert.calledWith(this.resortStub, { after: 1 });
      });
    });

    context('bottom', function() {
      it('calls resort with the correct values', function() {
        ProductStore.internals.updateItemPriority(1, 1, 'bottom');
        sinon.assert.calledWith(this.resortStub, { before: 3 });
      });
    });
  });

  describe('getAll', function() {
    it('only returns active products', function() {
      let products = ProductStore.__get__('products');
      products.reset([{ name: 'sprintly', archived: false, id: 1 }, { name: 'old project', archived: true, id: 2 }]);
      let activeProducts = ProductStore.getAll();
      assert.lengthOf(activeProducts, 1);
      products.reset([]);
    });
  });

  xdescribe('getItemsForProduct', function() {
    it('returns an items collection', function() {

    });

    it('updates the items collection filter config', function() {

    });
  });
});
<commit_msg>tests for reassignment on status changes<commit_after>var $ = require('jquery');
var _ = require('lodash');
var ProductStore = require('../product-store');
var Backbone = require('backdash');
var assert = require('chai').assert;
var sinon = require('sinon');

describe('ProductStore', function() {

  describe('internals.initProducts', function() {
    beforeEach(function() {
      this.sinon = sinon.sandbox.create();
      this.products = ProductStore.__get__('products');
      this.user = ProductStore.__get__('user');
      this.FiltersAction = ProductStore.__get__('FiltersAction');
      this.productsTriggerStub = sinon.stub()
      ProductStore.__set__('products', {
        fetch: () => true,
        trigger: this.productsTriggerStub,
        get: sinon.stub()
      });
      ProductStore.__set__('user', {
        fetch: () => true
      });
      this.filtersInitStub = sinon.stub();
      ProductStore.__set__('FiltersAction', {
        init: this.filtersInitStub
      });
    });

    afterEach(function() {
      ProductStore.__set__('products', this.products);
      ProductStore.__set__('user', this.user);
      ProductStore.__set__('FiltersAction', this.FiltersAction);
      this.sinon.restore();
    });

    it('should return a promise', function() {
      return ProductStore.internals.initProducts();
    });

    context('after successful fetch', function() {
      beforeEach(function() {
        this.createSubscriptionStub = this.sinon.stub(ProductStore.internals, 'createSubscription');
      });

      it('initializes filters data', function(done) {
        ProductStore.internals.initProducts(1).then(() => {
          assert.isTrue(this.filtersInitStub.called);
          done();
        })
      });

      it('subscribes to product events', function(done) {
        ProductStore.internals.initProducts(1).then(() => {
          assert.isTrue(this.createSubscriptionStub.called);
          done();
        })
      });

      it('triggers a change event', function(done) {
        ProductStore.internals.initProducts(1).then(() => {
          assert.isTrue(this.productsTriggerStub.called);
          done();
        });
      });

      context('no product id', function() {
        it('triggers a change event', function(done) {
          ProductStore.internals.initProducts(1).then(() => {
            assert.isTrue(this.productsTriggerStub.called);
            done();
          });
        });
      });
    });
  });

  describe('internals.updateItem', function() {
    beforeEach(function() {
      this.user = ProductStore.__get__('user');
      this.products = ProductStore.__get__('products');
      this.product = this.products.add({ id: 1 });
      this.item = this.product.items.add(
        { id: 1, status: 'in-progress' }
      );
      this.item.save = sinon.stub();
    });

    afterEach(function() {
      this.product.items.reset();
    });

    it('unsets the close reason if status is changing', function() {
      this.item.set({ close_reason: 'fixed' });
      ProductStore.internals.updateItem(1, 1, { status: 'in-progress' });
      assert.isUndefined(this.item.get('close_reason'));
    });

    describe('assigns the issue to the user when upgrading backlog or someday items to in-progress or completed', function() {
      beforeEach(function() {
        this.user.set({ id: 2 });
      })

      _.each(['backlog', 'someday'], (status) => {
        _.each(['in-progress', 'completed'], (newStatus) => {
          it(`Moving item from ${status} to ${newStatus}`, function() {
            this.item.set({ status: status, assigned_to: 1 });
            ProductStore.internals.updateItem(1, 1, { status: newStatus });
            sinon.assert.calledWith(this.item.save, { assigned_to: 2, status: newStatus });
          });
        });
      });
    });

    it('calls item.save', function() {
      ProductStore.internals.updateItem(1, 1, { status: 'completed' });
      sinon.assert.calledWith(this.item.save, { status: 'completed' });
    });
  });

  describe('internals.loadMoreItems', function() {
    beforeEach(function() {
      this.collection = new Backbone.Collection();
      this.collection.config = new Backbone.Model({
        status: 'backlog',
        offset: 0
      });
    });

    it('should return a promise', function() {
      var spy = sinon.spy(this.collection, 'trigger');

      this.collection.fetch = () => {
        return $.Deferred().resolve({length: 0});
      };

      ProductStore.internals.loadMoreItems(this.collection);
      sinon.assert.calledOnce(spy);
    });

    it('should emit a change event and pass an object containing the response item count', function() {
      var spy = sinon.spy(this.collection, 'trigger');

      this.collection.fetch = () => {
        return $.Deferred().resolve({length: 16});
      };

      ProductStore.internals.loadMoreItems(this.collection);
      sinon.assert.calledWith(spy, 'change', {count: 16});
    });
  });

  describe('updateItemPriority', function() {
    beforeEach(function() {
      this.products = ProductStore.__get__('products');
      this.product = this.products.add({ id: 1 });
      this.backlog = this.product.getItemsByStatus('backlog');
      this.resortStub = sinon.stub(this.product.ItemModel.prototype, 'resort');
      this.backlog.reset([
        { number: 1, status: 'backlog', product: { id: 1 }, sort: 1 },
        { number: 2, status: 'backlog', product: { id: 1 }, sort: 2 },
        { number: 3, status: 'backlog', product: { id: 1 }, sort: 3 }
      ]);
    });

    afterEach(function() {
      this.resortStub.restore();
    });

    after(function() {
      this.backlog.reset();
    });

    it('throws when unexpected sort argument encountered', function() {
      assert.throws(() => ProductStore.internals.updateItemPriority(1,1,'sideways'), /Invalid priority direction/);
    });

    context('up', function() {
      it('calls resort with the expected before and after values when both exist', function() {
        ProductStore.internals.updateItemPriority(1, 3, 'up');
        sinon.assert.calledWith(this.resortStub, { before: 2, after: 1 });
      })

      it('call resort with the expected arguments when no "after" item is found', function() {
        ProductStore.internals.updateItemPriority(1, 2, 'up');
        sinon.assert.calledWith(this.resortStub, { after: 1 });
      })
    });

    context('down', function() {
      it('calls resort with the expected before and after values when both "before" and "after" items exist', function() {
        ProductStore.internals.updateItemPriority(1, 1, 'down');
        sinon.assert.calledWith(this.resortStub, { before: 3, after: 2 });
      })

      it('call resort with the expected arguments when no "before" item is found', function() {
        ProductStore.internals.updateItemPriority(1, 2, 'down');
        sinon.assert.calledWith(this.resortStub, { before: 3 });
      })
    });

    context('top', function() {
      it('calls resort with the correct values', function() {
        ProductStore.internals.updateItemPriority(1, 3, 'top');
        sinon.assert.calledWith(this.resortStub, { after: 1 });
      });
    });

    context('bottom', function() {
      it('calls resort with the correct values', function() {
        ProductStore.internals.updateItemPriority(1, 1, 'bottom');
        sinon.assert.calledWith(this.resortStub, { before: 3 });
      });
    });
  });

  describe('getAll', function() {
    it('only returns active products', function() {
      let products = ProductStore.__get__('products');
      products.reset([{ name: 'sprintly', archived: false, id: 1 }, { name: 'old project', archived: true, id: 2 }]);
      let activeProducts = ProductStore.getAll();
      assert.lengthOf(activeProducts, 1);
      products.reset([]);
    });
  });

  xdescribe('getItemsForProduct', function() {
    it('returns an items collection', function() {

    });

    it('updates the items collection filter config', function() {

    });
  });
});
<|endoftext|>


<commit_before>'use strict';

// Offers controller
angular.module('offers').controller('OffersController', ['$scope', '$stateParams', '$location', '$q', 'Authentication', 'Offers', 'TableSettings', 'OffersForm', 'Sites',
	function ($scope, $stateParams, $location, $q, Authentication, Offers, TableSettings, OffersForm, Sites) {
		$scope.authentication = Authentication;
		$scope.tableParams = TableSettings.getParams(Offers);
		$scope.offer = {};

		$scope.setFormFields = function (disabled) {
			$scope.formFields = OffersForm.getFormFields(disabled);
		};

		// Create new Offer
		$scope.create = function () {
			var offer = new Offers($scope.offer);

			// Redirect after save
			offer.$save(function (response) {
				$location.path('offers/' + response._id);
			}, function (errorResponse) {
				$scope.error = errorResponse.data.message;
			});
		};

		// Remove existing Offer
		$scope.remove = function (offer) {

			if (offer) {
				offer = Offers.get({
					offerId: offer._id
				}, function () {
					offer.$remove(function() {
				      $scope.tableParams.reload();
				    });
				});

			}
			else {
				$scope.offer.$remove(function () {
					$location.path('offers');
				});
			}

		};

		// Update existing Offer
		$scope.update = function () {
			var offer = $scope.offer;

			offer.$update(function () {
				$location.path('offers/' + offer._id);
			}, function (errorResponse) {
				$scope.error = errorResponse.data.message;
			});
		};

		$scope.toViewOffer = function () {
			$scope.offer = Offers.get({
				offerId: $stateParams.offerId
			});
			$scope.setFormFields(true);
		};

		$scope.toEditOffer = function () {
			$scope.offer = Offers.get({
				offerId: $stateParams.offerId
			});
			$scope.setFormFields(false);
		};

		$scope.sites = function () {
            var def = $q.defer(),
                sites = [];

            Sites.all(function (res) {
            	console.log(res);

				res.$promise.then(function (result) {
				    angular.forEach(result, function (item) {
                        sites.push({
                            'id': item.url,
                            'title': item.name
                        });
	                });

		            def.resolve(sites);
				});

            });

            return def;
        };

	}

]);
<commit_msg>remove obsolete debug log message<commit_after>'use strict';

// Offers controller
angular.module('offers').controller('OffersController', ['$scope', '$stateParams', '$location', '$q', 'Authentication', 'Offers', 'TableSettings', 'OffersForm', 'Sites',
	function ($scope, $stateParams, $location, $q, Authentication, Offers, TableSettings, OffersForm, Sites) {
		$scope.authentication = Authentication;
		$scope.tableParams = TableSettings.getParams(Offers);
		$scope.offer = {};

		$scope.setFormFields = function (disabled) {
			$scope.formFields = OffersForm.getFormFields(disabled);
		};

		// Create new Offer
		$scope.create = function () {
			var offer = new Offers($scope.offer);

			// Redirect after save
			offer.$save(function (response) {
				$location.path('offers/' + response._id);
			}, function (errorResponse) {
				$scope.error = errorResponse.data.message;
			});
		};

		// Remove existing Offer
		$scope.remove = function (offer) {

			if (offer) {
				offer = Offers.get({
					offerId: offer._id
				}, function () {
					offer.$remove(function() {
				      $scope.tableParams.reload();
				    });
				});

			}
			else {
				$scope.offer.$remove(function () {
					$location.path('offers');
				});
			}

		};

		// Update existing Offer
		$scope.update = function () {
			var offer = $scope.offer;

			offer.$update(function () {
				$location.path('offers/' + offer._id);
			}, function (errorResponse) {
				$scope.error = errorResponse.data.message;
			});
		};

		$scope.toViewOffer = function () {
			$scope.offer = Offers.get({
				offerId: $stateParams.offerId
			});
			$scope.setFormFields(true);
		};

		$scope.toEditOffer = function () {
			$scope.offer = Offers.get({
				offerId: $stateParams.offerId
			});
			$scope.setFormFields(false);
		};

		$scope.sites = function () {
            var def = $q.defer(),
                sites = [];

            Sites.all(function (res) {
				res.$promise.then(function (result) {
				    angular.forEach(result, function (item) {
                        sites.push({
                            'id': item.url,
                            'title': item.name
                        });
	                });

		            def.resolve(sites);
				});
            });

            return def;
        };

	}

]);
<|endoftext|>


<commit_before>/**
 * @author bhouston / http://exocortex.com
 */

THREE.Box2 = function ( min, max ) {

	this.min = min !== undefined ? min.clone() : new THREE.Vector2( Infinity, Infinity );
	this.max = max !== undefined ? max.clone() : new THREE.Vector2( -Infinity, -Infinity );

};

THREE.Box2.prototype = {

	constructor: THREE.Box2,

	set: function ( min, max ) {

		this.min.copy( min );
		this.max.copy( max );

		return this;

	},

	setFromPoints: function ( points ) {

		if ( points.length > 0 ) {

			var point = points[ 0 ];

			this.min.copy( point );
			this.max.copy( point );

			for ( var i = 1, il = points.length; i < il; i ++ ) {

				point = points[ i ];

				if ( point.x < this.min.x ) {

					this.min.x = point.x;

				} else if ( point.x > this.max.x ) {

					this.max.x = point.x;

				}

				if ( point.y < this.min.y ) {

					this.min.y = point.y;

				} else if ( point.y > this.max.y ) {

					this.max.y = point.y;

				}

			}

		} else {

			this.makeEmpty();

		}

		return this;

	},

	setFromCenterAndSize: function ( center, size ) {

		var halfSize = THREE.Box2.__v1.copy( size ).multiplyScalar( 0.5 );
		this.min.copy( center ).subSelf( halfSize );
		this.max.copy( center ).addSelf( halfSize );

		return this;

	},

	copy: function ( box ) {

		this.min.copy( box.min );
		this.max.copy( box.max );

		return this;

	},

	makeEmpty: function () {

		this.min.x = this.min.y = Infinity;
		this.max.x = this.max.y = -Infinity;

		return this;

	},

	empty: function () {

		// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes

		return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );

	},

	center: function ( optionalTarget ) {

		var result = optionalTarget || new THREE.Vector2();
		return result.add( this.min, this.max ).multiplyScalar( 0.5 );

	},

	size: function ( optionalTarget ) {

		var result = optionalTarget || new THREE.Vector2();
		return result.sub( this.max, this.min );

	},

	expandByPoint: function ( point ) {

		this.min.minSelf( point );
		this.max.maxSelf( point );

		return this;
	},

	expandByVector: function ( vector ) {

		this.min.subSelf( vector );
		this.max.addSelf( vector );

		return this;
	},

	expandByScalar: function ( scalar ) {

		this.min.addScalar( -scalar );
		this.max.addScalar( scalar );

		return this;
	},

	containsPoint: function ( point ) {

		if ( ( this.min.x <= point.x ) && ( point.x <= this.max.x ) &&
			 ( this.min.y <= point.y ) && ( point.y <= this.max.y ) ) {

			return true;

		}

		return false;

	},

	containsBox: function ( box ) {

		if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
			 ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {

			return true;

		}

		return false;

	},

	getParameter: function ( point ) {

		// This can potentially have a divide by zero if the box
		// has a size dimension of 0.

		return new THREE.Vector2(
			( point.x - this.min.x ) / ( this.max.x - this.min.x ),
			( point.y - this.min.y ) / ( this.max.y - this.min.y )
		);

	},

	isIntersectionBox: function ( box ) {

		// using 6 splitting planes to rule out intersections.

		if ( ( box.max.x < this.min.x ) ||
		     ( box.min.x > this.max.x ) ||
		     ( box.max.y < this.min.y ) ||
		     ( box.min.y > this.max.y ) ) {

			return false;

		}

		return true;

	},

	clampPoint: function ( point, optionalTarget ) {

		var result = optionalTarget || new THREE.Vector2();
		return result.copy( point ).clampSelf( this.min, this.max );

	},

	distanceToPoint: function ( point ) {

		var clampedPoint = THREE.Box2.__v1.copy( point ).clampSelf( this.min, this.max );
		return clampedPoint.subSelf( point ).length();

	},

	intersect: function ( box ) {

		this.min.maxSelf( box.min );
		this.max.minSelf( box.max );

		return this;

	},

	union: function ( box ) {

		this.min.minSelf( box.min );
		this.max.maxSelf( box.max );

		return this;

	},

	translate: function ( offset ) {

		this.min.addSelf( offset );
		this.max.addSelf( offset );

		return this;

	},

	equals: function ( box ) {

		return box.min.equals( this.min ) && box.max.equals( this.max );

	},

	clone: function () {

		return new THREE.Box2().copy( this );

	}

};

THREE.Box2.__v1 = new THREE.Vector2();
<commit_msg>Aesthetic tweaks to Box2.<commit_after>/**
 * @author bhouston / http://exocortex.com
 */

THREE.Box2 = function ( min, max ) {

	this.min = min !== undefined ? min.clone() : new THREE.Vector2( Infinity, Infinity );
	this.max = max !== undefined ? max.clone() : new THREE.Vector2( -Infinity, -Infinity );

};

THREE.Box2.prototype = {

	constructor: THREE.Box2,

	set: function ( min, max ) {

		this.min.copy( min );
		this.max.copy( max );

		return this;

	},

	setFromPoints: function ( points ) {

		if ( points.length > 0 ) {

			var point = points[ 0 ];

			this.min.copy( point );
			this.max.copy( point );

			for ( var i = 1, il = points.length; i < il; i ++ ) {

				point = points[ i ];

				if ( point.x < this.min.x ) {

					this.min.x = point.x;

				} else if ( point.x > this.max.x ) {

					this.max.x = point.x;

				}

				if ( point.y < this.min.y ) {

					this.min.y = point.y;

				} else if ( point.y > this.max.y ) {

					this.max.y = point.y;

				}

			}

		} else {

			this.makeEmpty();

		}

		return this;

	},

	setFromCenterAndSize: function ( center, size ) {

		var halfSize = THREE.Box2.__v1.copy( size ).multiplyScalar( 0.5 );
		this.min.copy( center ).subSelf( halfSize );
		this.max.copy( center ).addSelf( halfSize );

		return this;

	},

	copy: function ( box ) {

		this.min.copy( box.min );
		this.max.copy( box.max );

		return this;

	},

	makeEmpty: function () {

		this.min.x = this.min.y = Infinity;
		this.max.x = this.max.y = -Infinity;

		return this;

	},

	empty: function () {

		// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes

		return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );

	},

	center: function ( optionalTarget ) {

		var result = optionalTarget || new THREE.Vector2();
		return result.add( this.min, this.max ).multiplyScalar( 0.5 );

	},

	size: function ( optionalTarget ) {

		var result = optionalTarget || new THREE.Vector2();
		return result.sub( this.max, this.min );

	},

	expandByPoint: function ( point ) {

		this.min.minSelf( point );
		this.max.maxSelf( point );

		return this;
	},

	expandByVector: function ( vector ) {

		this.min.subSelf( vector );
		this.max.addSelf( vector );

		return this;
	},

	expandByScalar: function ( scalar ) {

		this.min.addScalar( -scalar );
		this.max.addScalar( scalar );

		return this;
	},

	containsPoint: function ( point ) {

		if ( ( this.min.x <= point.x ) && ( point.x <= this.max.x ) &&
		     ( this.min.y <= point.y ) && ( point.y <= this.max.y ) ) {

			return true;

		}

		return false;

	},

	containsBox: function ( box ) {

		if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
		     ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {

			return true;

		}

		return false;

	},

	getParameter: function ( point ) {

		// This can potentially have a divide by zero if the box
		// has a size dimension of 0.

		return new THREE.Vector2(
			( point.x - this.min.x ) / ( this.max.x - this.min.x ),
			( point.y - this.min.y ) / ( this.max.y - this.min.y )
		);

	},

	isIntersectionBox: function ( box ) {

		// using 6 splitting planes to rule out intersections.

		if ( ( box.max.x < this.min.x ) ||
		     ( box.min.x > this.max.x ) ||
		     ( box.max.y < this.min.y ) ||
		     ( box.min.y > this.max.y ) ) {

			return false;

		}

		return true;

	},

	clampPoint: function ( point, optionalTarget ) {

		var result = optionalTarget || new THREE.Vector2();
		return result.copy( point ).clampSelf( this.min, this.max );

	},

	distanceToPoint: function ( point ) {

		var clampedPoint = THREE.Box2.__v1.copy( point ).clampSelf( this.min, this.max );
		return clampedPoint.subSelf( point ).length();

	},

	intersect: function ( box ) {

		this.min.maxSelf( box.min );
		this.max.minSelf( box.max );

		return this;

	},

	union: function ( box ) {

		this.min.minSelf( box.min );
		this.max.maxSelf( box.max );

		return this;

	},

	translate: function ( offset ) {

		this.min.addSelf( offset );
		this.max.addSelf( offset );

		return this;

	},

	equals: function ( box ) {

		return box.min.equals( this.min ) && box.max.equals( this.max );

	},

	clone: function () {

		return new THREE.Box2().copy( this );

	}

};

THREE.Box2.__v1 = new THREE.Vector2();
<|endoftext|>


<commit_before>var Plotly = require('../../plotly'),
    isNumeric = require('../../isnumeric');

var Gl3dLayout = {};

module.exports = Gl3dLayout;

Gl3dLayout.layoutAttributes = {
    bgcolor: {
        type: 'color',
        dflt: 'rgba(0,0,0,0)'
    },
    cameraposition: {
        type: 'data_array'
    },
    domain: {
        x: [
            {type: 'number', min: 0, max: 1},
            {type: 'number', min: 0, max: 1}
        ],
        y:[
            {type: 'number', min: 0, max: 1, dflt: 0},
            {type: 'number', min: 0, max: 1, dflt: 1}
        ]
    },
    _nestedModules: {  // nested module coupling
        'xaxis': 'Gl3dAxes',
        'yaxis': 'Gl3dAxes',
        'zaxis': 'Gl3dAxes'
    },
    aspectmode: {
        type: 'enumerated',
        values: ['auto', 'cube', 'data', 'manual'],
        dflt: 'auto'
    },
    aspectratio: { // must be positive (0's are coerced to 1)
        x: {
            type: 'number',
            min: 0
        },
        y: {
            type: 'number',
            min: 0
        },
        z: {
            type: 'number',
            min: 0
        }
    }
};

Gl3dLayout.supplyLayoutDefaults = function (layoutIn, layoutOut, fullData) {

    if (!layoutOut._hasGL3D) return;

    var scenes = [];
    var i;

    for (i = 0; i < fullData.length; ++i) {
        var d = fullData[i];
        if (Plotly.Plots.isGL3D(d.type)) {
            if (scenes.indexOf(d.scene) === -1) {
                scenes.push(d.scene);
            }
        }
    }

    // until they play better together
    delete layoutOut.xaxis;
    delete layoutOut.yaxis;

    // Get number of scenes to compute default scene domain
    var scenesLength = scenes.length;

    for (i = 0; i < scenesLength; ++i) {
        var scene = scenes[i];
        /*
         * Scene numbering proceeds as follows
         * scene
         * scene2
         * scene3
         *
         * and d.scene will be undefined or some number or number string
         *
         * Also write back a blank scene object to user layout so that some
         * attributes like aspectratio can be written back dynamically.
         */
        var sceneLayoutIn;
        if (scene in layoutIn) sceneLayoutIn = layoutIn[scene];
        else layoutIn[scene] = sceneLayoutIn = {};

        var sceneLayoutOut = layoutOut[scene] || {};

        function coerce(attr, dflt) {
            return Plotly.Lib.coerce(sceneLayoutIn, sceneLayoutOut,
                                     Gl3dLayout.layoutAttributes, attr, dflt);
        }

        coerce('bgcolor');
        coerce('cameraposition');
        coerce('domain.x[0]', i / scenesLength);
        coerce('domain.x[1]', (i+1) / scenesLength);
        coerce('domain.y[0]');
        coerce('domain.y[1]');

        coerce('aspectmode');

        /*
         * coerce to positive number (min 0) but also do not accept 0 (>0 not >=0)
         * note that 0's go false with the !! call
         */
        var hasAspect = true;
        hasAspect = hasAspect && !!coerce('aspectratio.x');
        hasAspect = hasAspect && !!coerce('aspectratio.y');
        hasAspect = hasAspect && !!coerce('aspectratio.z');

        if (!hasAspect) {
            sceneLayoutIn.aspectratio = sceneLayoutOut.aspectratio = {x: 1, y: 1, z: 1};
            sceneLayoutOut.aspectmode = 'auto';
        }

         /*
          * scene arrangements need to be implemented: For now just splice
          * along the horizontal direction. ie.
          * x:[0,1] -> x:[0,0.5], x:[0.5,1] ->
          *     x:[0, 0.333] x:[0.333,0.666] x:[0.666, 1]
          */
        Plotly.Gl3dAxes.supplyLayoutDefaults(sceneLayoutIn, sceneLayoutOut, {
            font: layoutOut.font,
            scene: scene,
            data: fullData
        });

        layoutOut[scene] = sceneLayoutOut;
    }
};

// Clean scene ids, 'scene1' -> 'scene'
Gl3dLayout.cleanId = function cleanId(id) {
    if (!id.match(/^scene[0-9]*$/)) return;

    var sceneNum = id.substr(5);
    if (sceneNum === '1') sceneNum = '';

    return 'scene' + sceneNum;
};
<commit_msg>valid ratio data and no mode results in mode == manual<commit_after>var Plotly = require('../../plotly'),
    isNumeric = require('../../isnumeric');

var Gl3dLayout = {};

module.exports = Gl3dLayout;

Gl3dLayout.layoutAttributes = {
    bgcolor: {
        type: 'color',
        dflt: 'rgba(0,0,0,0)'
    },
    cameraposition: {
        type: 'data_array'
    },
    domain: {
        x: [
            {type: 'number', min: 0, max: 1},
            {type: 'number', min: 0, max: 1}
        ],
        y:[
            {type: 'number', min: 0, max: 1, dflt: 0},
            {type: 'number', min: 0, max: 1, dflt: 1}
        ]
    },
    _nestedModules: {  // nested module coupling
        'xaxis': 'Gl3dAxes',
        'yaxis': 'Gl3dAxes',
        'zaxis': 'Gl3dAxes'
    },
    aspectmode: {
        type: 'enumerated',
        values: ['auto', 'cube', 'data', 'manual'],
        dflt: 'auto'
    },
    aspectratio: { // must be positive (0's are coerced to 1)
        x: {
            type: 'number',
            min: 0
        },
        y: {
            type: 'number',
            min: 0
        },
        z: {
            type: 'number',
            min: 0
        }
    }
};

Gl3dLayout.supplyLayoutDefaults = function (layoutIn, layoutOut, fullData) {

    if (!layoutOut._hasGL3D) return;

    var scenes = [];
    var attributes = Gl3dLayout.layoutAttributes;
    var i;

    for (i = 0; i < fullData.length; ++i) {
        var d = fullData[i];
        if (Plotly.Plots.isGL3D(d.type)) {
            if (scenes.indexOf(d.scene) === -1) {
                scenes.push(d.scene);
            }
        }
    }

    // until they play better together
    delete layoutOut.xaxis;
    delete layoutOut.yaxis;

    // Get number of scenes to compute default scene domain
    var scenesLength = scenes.length;

    for (i = 0; i < scenesLength; ++i) {
        var scene = scenes[i];
        /*
         * Scene numbering proceeds as follows
         * scene
         * scene2
         * scene3
         *
         * and d.scene will be undefined or some number or number string
         *
         * Also write back a blank scene object to user layout so that some
         * attributes like aspectratio can be written back dynamically.
         */
        var sceneLayoutIn;
        if (scene in layoutIn) sceneLayoutIn = layoutIn[scene];
        else layoutIn[scene] = sceneLayoutIn = {};

        var sceneLayoutOut = layoutOut[scene] || {};

        function coerce(attr, dflt) {
            return Plotly.Lib.coerce(sceneLayoutIn, sceneLayoutOut,
                                     attributes, attr, dflt);
        }

        coerce('bgcolor');
        coerce('cameraposition');
        coerce('domain.x[0]', i / scenesLength);
        coerce('domain.x[1]', (i+1) / scenesLength);
        coerce('domain.y[0]');
        coerce('domain.y[1]');

        /*
         * coerce to positive number (min 0) but also do not accept 0 (>0 not >=0)
         * note that 0's go false with the !! call
         */
        var hasAspect = true;
        hasAspect = hasAspect && !!coerce('aspectratio.x');
        hasAspect = hasAspect && !!coerce('aspectratio.y');
        hasAspect = hasAspect && !!coerce('aspectratio.z');

        var aspectRatio = coerce('aspectmode');

        if (hasAspect && attributes.aspectmode.values.indexOf(sceneLayoutIn.aspectmode) === -1) {

            // invalid aspectmode provided with valid aspectratio. Assume mode == 'manual'
            sceneLayoutOut.aspectmode = 'manual';

        } else if (!hasAspect) {

            /*
             * we need aspectratio object in all the Layouts as it is dynamically set
             * in the calculation steps, ie, we cant set the correct data now, it happens later.
             */
            sceneLayoutIn.aspectratio = sceneLayoutOut.aspectratio = {x: 1, y: 1, z: 1};

            // if manual data provided and manual mode selected but data is bad, switch to auto
            if (aspectRatio === 'manual') sceneLayoutOut.aspectmode = 'auto';
        }

         /*
          * scene arrangements need to be implemented: For now just splice
          * along the horizontal direction. ie.
          * x:[0,1] -> x:[0,0.5], x:[0.5,1] ->
          *     x:[0, 0.333] x:[0.333,0.666] x:[0.666, 1]
          */
        Plotly.Gl3dAxes.supplyLayoutDefaults(sceneLayoutIn, sceneLayoutOut, {
            font: layoutOut.font,
            scene: scene,
            data: fullData
        });

        layoutOut[scene] = sceneLayoutOut;
    }
};

// Clean scene ids, 'scene1' -> 'scene'
Gl3dLayout.cleanId = function cleanId(id) {
    if (!id.match(/^scene[0-9]*$/)) return;

    var sceneNum = id.substr(5);
    if (sceneNum === '1') sceneNum = '';

    return 'scene' + sceneNum;
};
<|endoftext|>


<commit_before>import * as d3 from 'd3';
import { osmEntity } from '../osm/index';
import { svgPointTransform } from './index';

export function svgVertices(projection, context) {
    var radiuses = {
        //       z16-, z17, z18+, tagged
        shadow: [6,    7.5,   7.5,  11.5],
        stroke: [2.5,  3.5,   3.5,  7],
        fill:   [1,    1.5,   1.5,  1.5]
    };

    var hover;

    function siblingAndChildVertices(ids, graph, extent) {
        var vertices = {};

        function addChildVertices(entity) {
            if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
                var i;
                if (entity.type === 'way') {
                    for (i = 0; i < entity.nodes.length; i++) {
                        addChildVertices(graph.entity(entity.nodes[i]));
                    }
                } else if (entity.type === 'relation') {
                    for (i = 0; i < entity.members.length; i++) {
                        var member = context.hasEntity(entity.members[i].id);
                        if (member) {
                            addChildVertices(member);
                        }
                    }
                } else if (entity.intersects(extent, graph)) {
                    vertices[entity.id] = entity;
                }
            }
        }

        ids.forEach(function(id) {
            var entity = context.hasEntity(id);
            if (entity && entity.type === 'node') {
                vertices[entity.id] = entity;
                context.graph().parentWays(entity).forEach(function(entity) {
                    addChildVertices(entity);
                });
            } else if (entity) {
                addChildVertices(entity);
            }
        });

        return vertices;
    }

    function draw(selection, vertices, klass, graph, zoom) {

        function icon(entity) {
            if (entity.id in icons) return icons[entity.id];
            icons[entity.id] =
                entity.hasInterestingTags() &&
                context.presets().match(entity, graph).icon;
            return icons[entity.id];
        }

        function setClass(klass) {
            return function(entity) {
                this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
            };
        }

        function setAttributes(selection) {
            ['shadow','stroke','fill'].forEach(function(klass) {
                var rads = radiuses[klass];
                selection.selectAll('.' + klass)
                    .each(function(entity) {
                        var i = z && icon(entity),
                            c = i ? 0.5 : 0,
                            r = rads[i ? 3 : z];
                        this.setAttribute('cx', c);
                        this.setAttribute('cy', -c);
                        this.setAttribute('r', r);
                        if (i && klass === 'fill') {
                            this.setAttribute('visibility', 'hidden');
                        } else {
                            this.removeAttribute('visibility');
                        }
                    });
            });

            selection.selectAll('use')
                .each(function() {
                    if (z) {
                        this.removeAttribute('visibility');
                    } else {
                        this.setAttribute('visibility', 'hidden');
                    }
                });
        }

        var icons = {},
            z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);

        var groups = selection
            .data(vertices, osmEntity.key);

        groups.exit()
            .remove();

        var enter = groups.enter()
            .append('g')
            .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });

        enter.append('circle')
            .each(setClass('shadow'));

        enter.append('circle')
            .each(setClass('stroke'));

        // Vertices with icons get a `use`.
        enter.filter(function(d) { return icon(d); })
            .append('use')
            .attr('transform', 'translate(-5, -6)')
            .attr('xlink:href', function(d) { return '#' + icon(d) + '-11'; })
            .attr('width', '11px')
            .attr('height', '11px')
            .each(setClass('icon'));

        // Vertices with tags get a fill.
        enter.filter(function(d) { return d.hasInterestingTags(); })
            .append('circle')
            .each(setClass('fill'));

        groups
            .merge(enter)
            .attr('transform', svgPointTransform(projection))
            .classed('shared', function(entity) { return graph.isShared(entity); })
            .call(setAttributes);
    }

    function drawVertices(selection, graph, entities, filter, extent, zoom) {
        var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
            wireframe = context.surface().classed('fill-wireframe'),
            vertices = [];

        for (var i = 0; i < entities.length; i++) {
            var entity = entities[i],
                geometry = entity.geometry(graph);

            if (wireframe && geometry === 'point') {
                vertices.push(entity);
                continue;
            }

            if (geometry !== 'vertex')
                continue;

            if (entity.id in selected ||
                entity.hasInterestingTags() ||
                entity.isIntersection(graph)) {
                vertices.push(entity);
            }
        }

        var layer = selection.selectAll('.layer-hit');
        layer.selectAll('g.vertex.vertex-persistent')
            .filter(filter)
            .call(draw, vertices, 'vertex-persistent', graph, zoom);

        drawHover(selection, graph, extent, zoom);
    }

    function drawHover(selection, graph, extent, zoom) {
        var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
        var layer = selection.selectAll('.layer-hit');

        layer.selectAll('g.vertex.vertex-hover')
            .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
    }

    drawVertices.drawHover = function(selection, graph, target, extent, zoom) {
        if (target === hover) return;
        hover = target;
        drawHover(selection, graph, extent, zoom);
    };

    return drawVertices;
}
<commit_msg>Always render vertices at endpoints (see #3775)<commit_after>import * as d3 from 'd3';
import { osmEntity } from '../osm/index';
import { svgPointTransform } from './index';

export function svgVertices(projection, context) {
    var radiuses = {
        //       z16-, z17, z18+, tagged
        shadow: [6,    7.5,   7.5,  11.5],
        stroke: [2.5,  3.5,   3.5,  7],
        fill:   [1,    1.5,   1.5,  1.5]
    };

    var hover;

    function siblingAndChildVertices(ids, graph, extent) {
        var vertices = {};

        function addChildVertices(entity) {
            if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
                var i;
                if (entity.type === 'way') {
                    for (i = 0; i < entity.nodes.length; i++) {
                        addChildVertices(graph.entity(entity.nodes[i]));
                    }
                } else if (entity.type === 'relation') {
                    for (i = 0; i < entity.members.length; i++) {
                        var member = context.hasEntity(entity.members[i].id);
                        if (member) {
                            addChildVertices(member);
                        }
                    }
                } else if (entity.intersects(extent, graph)) {
                    vertices[entity.id] = entity;
                }
            }
        }

        ids.forEach(function(id) {
            var entity = context.hasEntity(id);
            if (entity && entity.type === 'node') {
                vertices[entity.id] = entity;
                context.graph().parentWays(entity).forEach(function(entity) {
                    addChildVertices(entity);
                });
            } else if (entity) {
                addChildVertices(entity);
            }
        });

        return vertices;
    }

    function draw(selection, vertices, klass, graph, zoom) {

        function icon(entity) {
            if (entity.id in icons) return icons[entity.id];
            icons[entity.id] =
                entity.hasInterestingTags() &&
                context.presets().match(entity, graph).icon;
            return icons[entity.id];
        }

        function setClass(klass) {
            return function(entity) {
                this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
            };
        }

        function setAttributes(selection) {
            ['shadow','stroke','fill'].forEach(function(klass) {
                var rads = radiuses[klass];
                selection.selectAll('.' + klass)
                    .each(function(entity) {
                        var i = z && icon(entity),
                            c = i ? 0.5 : 0,
                            r = rads[i ? 3 : z];
                        this.setAttribute('cx', c);
                        this.setAttribute('cy', -c);
                        this.setAttribute('r', r);
                        if (i && klass === 'fill') {
                            this.setAttribute('visibility', 'hidden');
                        } else {
                            this.removeAttribute('visibility');
                        }
                    });
            });

            selection.selectAll('use')
                .each(function() {
                    if (z) {
                        this.removeAttribute('visibility');
                    } else {
                        this.setAttribute('visibility', 'hidden');
                    }
                });
        }

        var icons = {},
            z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);

        var groups = selection
            .data(vertices, osmEntity.key);

        groups.exit()
            .remove();

        var enter = groups.enter()
            .append('g')
            .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });

        enter.append('circle')
            .each(setClass('shadow'));

        enter.append('circle')
            .each(setClass('stroke'));

        // Vertices with icons get a `use`.
        enter.filter(function(d) { return icon(d); })
            .append('use')
            .attr('transform', 'translate(-5, -6)')
            .attr('xlink:href', function(d) { return '#' + icon(d) + '-11'; })
            .attr('width', '11px')
            .attr('height', '11px')
            .each(setClass('icon'));

        // Vertices with tags get a fill.
        enter.filter(function(d) { return d.hasInterestingTags(); })
            .append('circle')
            .each(setClass('fill'));

        groups
            .merge(enter)
            .attr('transform', svgPointTransform(projection))
            .classed('shared', function(entity) { return graph.isShared(entity); })
            .call(setAttributes);
    }

    function drawVertices(selection, graph, entities, filter, extent, zoom) {
        var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
            wireframe = context.surface().classed('fill-wireframe'),
            vertices = [];

        for (var i = 0; i < entities.length; i++) {
            var entity = entities[i],
                geometry = entity.geometry(graph);

            if (wireframe && geometry === 'point') {
                vertices.push(entity);
                continue;
            }

            if (geometry !== 'vertex')
                continue;

            if (entity.id in selected ||
                entity.hasInterestingTags() ||
                entity.isEndpoint(graph) ||
                entity.isIntersection(graph)) {
                vertices.push(entity);
            }
        }

        var layer = selection.selectAll('.layer-hit');
        layer.selectAll('g.vertex.vertex-persistent')
            .filter(filter)
            .call(draw, vertices, 'vertex-persistent', graph, zoom);

        drawHover(selection, graph, extent, zoom);
    }

    function drawHover(selection, graph, extent, zoom) {
        var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
        var layer = selection.selectAll('.layer-hit');

        layer.selectAll('g.vertex.vertex-hover')
            .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
    }

    drawVertices.drawHover = function(selection, graph, target, extent, zoom) {
        if (target === hover) return;
        hover = target;
        drawHover(selection, graph, extent, zoom);
    };

    return drawVertices;
}
<|endoftext|>


<commit_before>import {config} from '../config';

export class DashboardController {
  constructor ($http, $log, $scope, $rootScope) {
    'ngInject';

    $scope.version = {
      current: '',
      latest: ''
    };

    ahud.currentVersion(() => {
      $scope.version.current = ahud.current.string();
    });

    ahud.latestVersion(() => {
      $scope.version.latest = ahud.latest.string();
    });

    $scope.state = ahud.state();
    $scope.changeState = function() {
      ahud.install();
    };

    this.$http = $http;
    this.$log = $log;
    this.getNews();
  }

  getNews() {
    return;
    var self = this;
    let newsURL = 'http://steamcommunity.com/groups/ahud/rss';
    this.$http.get(newsURL).then(function(response) {
      //console.log(response.data);
      //self.onGetNews(response, self);
    }, this.onErr);

    //this.$http.get(config.API_URL + '/api/news').then(function(response) {
    //  self.onGetNews(response, self);
    //}, this.onErr);
  }

  onGetNews(response, self) {
    self.news = response.data;
    self.$log.debug(response.data);
  }

  onErr() {
    alert('Error: News can\'t be loaded, try again.');
  }
}
<commit_msg>Add settings at install<commit_after>import {config} from '../config';

export class DashboardController {
  constructor ($http, $log, $scope, $rootScope) {
    'ngInject';

    $scope.version = {
      current: '',
      latest: ''
    };

    ahud.currentVersion(() => {
      $scope.version.current = ahud.current.string();
    });

    ahud.latestVersion(() => {
      $scope.version.latest = ahud.latest.string();
    });

    $scope.state = ahud.state();
    $scope.changeState = function() {
      ahud.install({
        crosshairs: $rootScope.crosshairs,
        colors: $rootScope.colors
      }, () => {
        console.log('installed');
      });
    };

    this.$http = $http;
    this.$log = $log;
    this.getNews();
  }

  getNews() {
    return;
    var self = this;
    let newsURL = 'http://steamcommunity.com/groups/ahud/rss';
    this.$http.get(newsURL).then(function(response) {
      //console.log(response.data);
      //self.onGetNews(response, self);
    }, this.onErr);

    //this.$http.get(config.API_URL + '/api/news').then(function(response) {
    //  self.onGetNews(response, self);
    //}, this.onErr);
  }

  onGetNews(response, self) {
    self.news = response.data;
    self.$log.debug(response.data);
  }

  onErr() {
    alert('Error: News can\'t be loaded, try again.');
  }
}
<|endoftext|>


<commit_before>
var promisify = require('./_promisify.js')

module.exports = function (source, exports, methods) {
  methods.forEach(function (name) {
    if (deprecated(source, name)) return
    if (typeof source[name] === 'function')
      exports[name] = promisify(name, source[name])
  })

  // proxy the rest
  Object.keys(source).forEach(function (name) {
    if (deprecated(source, name)) return
    if (exports[name]) return
    exports[name] = source[name]
  })
}

function deprecated(source, name) {
  var desc = Object.getOwnPropertyDescriptor(source, name)
  if (!desc.get) return false
  if (desc.get.name === 'deprecated') return true
  return false
}
<commit_msg>fix undefined desc if name = lchown<commit_after>
var promisify = require('./_promisify.js')

module.exports = function (source, exports, methods) {
  methods.forEach(function (name) {
    if (deprecated(source, name)) return
    if (typeof source[name] === 'function')
      exports[name] = promisify(name, source[name])
  })

  // proxy the rest
  Object.keys(source).forEach(function (name) {
    if (deprecated(source, name)) return
    if (exports[name]) return
    exports[name] = source[name]
  })
}

function deprecated(source, name) {
  var desc = Object.getOwnPropertyDescriptor(source, name)
  if (!desc || !desc.get) return false
  if (desc.get.name === 'deprecated') return true
  return false
}
<|endoftext|>


<commit_before>"use strict";

// Create default indexes on users collection.
// Index only on "roles._id" is not needed because the combined index works for it as well.
Meteor.users._ensureIndex({'roles._id': 1, 'roles.partition': 1});
Meteor.users._ensureIndex({'roles.partition': 1});

/*
 * Publish logged-in user's roles so client-side checks can work.
 *
 * Use a named publish function so clients can check `ready()` state.
 */
Meteor.publish('_roles', function () {
  var loggedInUserId = this.userId,
      fields = {roles: 1};

  if (!loggedInUserId) {
    this.ready();
    return;
  }

  return Meteor.users.find({_id: loggedInUserId},
                           {fields: fields});
});

_.extend(Roles, {
  /**
   * @method _isNewRole
   * @param {Object} role `Meteor.roles` document.
   * @return {Boolean} Returns `true` if the `role` is in the new format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isNewRole: function (role) {
    return !_.has(role, 'name') && _.has(role, 'children');
  },

  /**
   * @method _isOldRole
   * @param {Object} role `Meteor.roles` document.
   * @return {Boolean} Returns `true` if the `role` is in the old format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isOldRole: function (role) {
    return _.has(role, 'name') && !_.has(role, 'children');
  },

  /**
   * @method _isNewField
   * @param {Array} roles `Meteor.users` document `roles` field.
   * @return {Boolean} Returns `true` if the `roles` field is in the new format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isNewField: function (roles) {
    return _.isArray(roles) && _.isObject(roles[0]);
  },

  /**
   * @method _isOldField
   * @param {Array} roles `Meteor.users` document `roles` field.
   * @return {Boolean} Returns `true` if the `roles` field is in the old format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isOldField: function (roles) {
    return (_.isArray(roles) && _.isString(roles[0])) || (_.isObject(roles) && !_.isArray(roles));
  },

  /**
   * @method _convertToNewRole
   * @param {Object} oldRole `Meteor.roles` document.
   * @return {Object} Converted `role` to the new format.
   * @for Roles
   * @private
   * @static
   */
  _convertToNewRole: function (oldRole) {
    if (!_.isString(oldRole.name)) throw new Error("Role name '" + oldRole.name + "' is not a string.");

    return {
      _id: oldRole.name,
      children: []
    };
  },

  /**
   * @method _convertToOldRole
   * @param {Object} newRole `Meteor.roles` document.
   * @return {Object} Converted `role` to the old format.
   * @for Roles
   * @private
   * @static
   */
  _convertToOldRole: function (newRole) {
    if (!_.isString(newRole._id)) throw new Error("Role name '" + newRole._id + "' is not a string.");

    return {
      name: newRole._id
    };
  },

  /**
   * @method _convertToNewField
   * @param {Array} oldRoles `Meteor.users` document `roles` field in the old format.
   * @return {Array} Converted `roles` to the new format.
   * @for Roles
   * @private
   * @static
   */
  _convertToNewField: function (oldRoles) {
    var roles = [];
    if (_.isArray(oldRoles)) {
      _.each(oldRoles, function (role, index) {
        if (!_.isString(role)) throw new Error("Role '" + role + "' is not a string.");

        roles.push({
          _id: role,
          partition: null,
          assigned: true
        })
      });
    }
    else if (_.isObject(oldRoles)) {
      _.each(oldRoles, function (rolesArray, group) {
        if (group === '__global_roles__') {
          group = null;
        }
        else {
          // unescape
          group = group.replace(/_/g, '.');
        }

        _.each(rolesArray, function (role, index) {
          if (!_.isString(role)) throw new Error("Role '" + role + "' is not a string.");

          roles.push({
            _id: role,
            partition: group,
            assigned: true
          })
        });
      })
    }
    return roles;
  },

  /**
   * @method _convertToOldField
   * @param {Array} newRoles `Meteor.users` document `roles` field in the new format.
   * @param {Boolean} usingGroups Should we use groups or not.
   * @return {Array} Converted `roles` to the old format.
   * @for Roles
   * @private
   * @static
   */
  _convertToOldField: function (newRoles, usingGroups) {
    var roles;

    if (usingGroups) {
      roles = {};
    }
    else {
      roles = [];
    }

    _.each(newRoles, function (userRole, index) {
      if (!_.isObject(userRole)) throw new Error("Role '" + userRole + "' is not an object.");

      // We assume that we are converting back a failed migration, so values can only be
      // what were valid values in 1.0. So no group names starting with $ and no subroles.

      if (userRole.partition) {
        if (!usingGroups) throw new Error("Role '" + userRole._id + "' with partition '" + userRole.partition + "' without enabled groups.");

        // escape
        var partition = userRole.partition.replace(/\./g, '_');

        if (partition[0] === '$') throw new Error("Group name '" + partition + "' start with $.");

        roles[partition] = roles[partition] || [];
        roles[partition].push(userRole._id);
      }
      else {
        if (usingGroups) {
          roles.__global_roles__ = roles.__global_roles__ || [];
          roles.__global_roles__.push(userRole._id);
        }
        else {
          roles.push(userRole._id);
        }
      }
    });
    return roles;
  },

  /**
   * @method _defaultUpdateUser
   * @param {Object} user `Meteor.users` document.
   * @param {Array|Object} roles Value to which user's `roles` field should be set.
   * @for Roles
   * @private
   * @static
   */
  _defaultUpdateUser: function (user, roles) {
    Meteor.users.update({
      _id: user._id,
      // making sure nothing changed in meantime
      roles: user.roles
    }, {
      $set: {roles: roles}
    });
  },

  /**
   * @method _defaultUpdateRole
   * @param {Object} oldRole Old `Meteor.roles` document.
   * @param {Object} newRole New `Meteor.roles` document.
   * @for Roles
   * @private
   * @static
   */
  _defaultUpdateRole: function (oldRole, newRole) {
    Meteor.roles.remove(oldRole._id);
    Meteor.roles.insert(newRole);
  },

  /**
   * Migrates `Meteor.users` and `Meteor.roles` to the new format.
   *
   * @method _forwardMigrate
   * @param {Function} updateUser Function which updates the user object. Default `_defaultUpdateUser`.
   * @param {Function} updateRole Function which updates the role object. Default `_defaultUpdateRole`.
   * @for Roles
   * @private
   * @static
   */
  _forwardMigrate: function (updateUser, updateRole) {
    updateUser = updateUser || Roles._defaultUpdateUser;
    updateRole = updateRole || Roles._defaultUpdateRole;

    Meteor.roles.find().forEach(function (role, index, cursor) {
      if (!Roles._isNewRole(role)) {
        updateRole(role, Roles._convertToNewRole(role));
      }
    });

    Meteor.users.find().forEach(function (user, index, cursor) {
      if (!Roles._isNewField(user.roles)) {
        updateUser(user, Roles._convertToNewField(user.roles));
      }
    });
  },

  /**
   * Migrates `Meteor.users` and `Meteor.roles` to the old format.
   *
   * We assume that we are converting back a failed migration, so values can only be
   * what were valid values in the old format. So no group names starting with `$` and
   * no subroles.
   *
   * @method _backwardMigrate
   * @param {Function} updateUser Function which updates the user object. Default `_defaultUpdateUser`.
   * @param {Function} updateRole Function which updates the role object. Default `_defaultUpdateRole`.
   * @param {Boolean} usingGroups Should we use groups or not.
   * @for Roles
   * @private
   * @static
   */
  _backwardMigrate: function (updateUser, updateRole, usingGroups) {
    updateUser = updateUser || Roles._defaultUpdateUser;
    updateRole = updateRole || Roles._defaultUpdateRole;

    Meteor.roles.find().forEach(function (role, index, cursor) {
      if (!Roles._isOldRole(role)) {
        updateRole(role, Roles._convertToOldRole(role));
      }
    });

    Meteor.users.find().forEach(function (user, index, cursor) {
      if (!Roles._isOldField(user.roles)) {
        updateUser(user, Roles._convertToOldField(user.roles, usingGroups));
      }
    });
  }
});
<commit_msg>fix #174 Drop mongodb roles index "name_1" in _forwardMigrate<commit_after>"use strict";

// Create default indexes on users collection.
// Index only on "roles._id" is not needed because the combined index works for it as well.
Meteor.users._ensureIndex({'roles._id': 1, 'roles.partition': 1});
Meteor.users._ensureIndex({'roles.partition': 1});

/*
 * Publish logged-in user's roles so client-side checks can work.
 *
 * Use a named publish function so clients can check `ready()` state.
 */
Meteor.publish('_roles', function () {
  var loggedInUserId = this.userId,
      fields = {roles: 1};

  if (!loggedInUserId) {
    this.ready();
    return;
  }

  return Meteor.users.find({_id: loggedInUserId},
                           {fields: fields});
});

_.extend(Roles, {
  /**
   * @method _isNewRole
   * @param {Object} role `Meteor.roles` document.
   * @return {Boolean} Returns `true` if the `role` is in the new format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isNewRole: function (role) {
    return !_.has(role, 'name') && _.has(role, 'children');
  },

  /**
   * @method _isOldRole
   * @param {Object} role `Meteor.roles` document.
   * @return {Boolean} Returns `true` if the `role` is in the old format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isOldRole: function (role) {
    return _.has(role, 'name') && !_.has(role, 'children');
  },

  /**
   * @method _isNewField
   * @param {Array} roles `Meteor.users` document `roles` field.
   * @return {Boolean} Returns `true` if the `roles` field is in the new format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isNewField: function (roles) {
    return _.isArray(roles) && _.isObject(roles[0]);
  },

  /**
   * @method _isOldField
   * @param {Array} roles `Meteor.users` document `roles` field.
   * @return {Boolean} Returns `true` if the `roles` field is in the old format.
   *                   If it is ambiguous or it is not, returns `false`.
   * @for Roles
   * @private
   * @static
   */
  _isOldField: function (roles) {
    return (_.isArray(roles) && _.isString(roles[0])) || (_.isObject(roles) && !_.isArray(roles));
  },

  /**
   * @method _convertToNewRole
   * @param {Object} oldRole `Meteor.roles` document.
   * @return {Object} Converted `role` to the new format.
   * @for Roles
   * @private
   * @static
   */
  _convertToNewRole: function (oldRole) {
    if (!_.isString(oldRole.name)) throw new Error("Role name '" + oldRole.name + "' is not a string.");

    return {
      _id: oldRole.name,
      children: []
    };
  },

  /**
   * @method _convertToOldRole
   * @param {Object} newRole `Meteor.roles` document.
   * @return {Object} Converted `role` to the old format.
   * @for Roles
   * @private
   * @static
   */
  _convertToOldRole: function (newRole) {
    if (!_.isString(newRole._id)) throw new Error("Role name '" + newRole._id + "' is not a string.");

    return {
      name: newRole._id
    };
  },

  /**
   * @method _convertToNewField
   * @param {Array} oldRoles `Meteor.users` document `roles` field in the old format.
   * @return {Array} Converted `roles` to the new format.
   * @for Roles
   * @private
   * @static
   */
  _convertToNewField: function (oldRoles) {
    var roles = [];
    if (_.isArray(oldRoles)) {
      _.each(oldRoles, function (role, index) {
        if (!_.isString(role)) throw new Error("Role '" + role + "' is not a string.");

        roles.push({
          _id: role,
          partition: null,
          assigned: true
        })
      });
    }
    else if (_.isObject(oldRoles)) {
      _.each(oldRoles, function (rolesArray, group) {
        if (group === '__global_roles__') {
          group = null;
        }
        else {
          // unescape
          group = group.replace(/_/g, '.');
        }

        _.each(rolesArray, function (role, index) {
          if (!_.isString(role)) throw new Error("Role '" + role + "' is not a string.");

          roles.push({
            _id: role,
            partition: group,
            assigned: true
          })
        });
      })
    }
    return roles;
  },

  /**
   * @method _convertToOldField
   * @param {Array} newRoles `Meteor.users` document `roles` field in the new format.
   * @param {Boolean} usingGroups Should we use groups or not.
   * @return {Array} Converted `roles` to the old format.
   * @for Roles
   * @private
   * @static
   */
  _convertToOldField: function (newRoles, usingGroups) {
    var roles;

    if (usingGroups) {
      roles = {};
    }
    else {
      roles = [];
    }

    _.each(newRoles, function (userRole, index) {
      if (!_.isObject(userRole)) throw new Error("Role '" + userRole + "' is not an object.");

      // We assume that we are converting back a failed migration, so values can only be
      // what were valid values in 1.0. So no group names starting with $ and no subroles.

      if (userRole.partition) {
        if (!usingGroups) throw new Error("Role '" + userRole._id + "' with partition '" + userRole.partition + "' without enabled groups.");

        // escape
        var partition = userRole.partition.replace(/\./g, '_');

        if (partition[0] === '$') throw new Error("Group name '" + partition + "' start with $.");

        roles[partition] = roles[partition] || [];
        roles[partition].push(userRole._id);
      }
      else {
        if (usingGroups) {
          roles.__global_roles__ = roles.__global_roles__ || [];
          roles.__global_roles__.push(userRole._id);
        }
        else {
          roles.push(userRole._id);
        }
      }
    });
    return roles;
  },

  /**
   * @method _defaultUpdateUser
   * @param {Object} user `Meteor.users` document.
   * @param {Array|Object} roles Value to which user's `roles` field should be set.
   * @for Roles
   * @private
   * @static
   */
  _defaultUpdateUser: function (user, roles) {
    Meteor.users.update({
      _id: user._id,
      // making sure nothing changed in meantime
      roles: user.roles
    }, {
      $set: {roles: roles}
    });
  },

  /**
   * @method _defaultUpdateRole
   * @param {Object} oldRole Old `Meteor.roles` document.
   * @param {Object} newRole New `Meteor.roles` document.
   * @for Roles
   * @private
   * @static
   */
  _defaultUpdateRole: function (oldRole, newRole) {
    Meteor.roles.remove(oldRole._id);
    Meteor.roles.insert(newRole);
  },

  /**
   * Migrates `Meteor.users` and `Meteor.roles` to the new format.
   *
   * @method _forwardMigrate
   * @param {Function} updateUser Function which updates the user object. Default `_defaultUpdateUser`.
   * @param {Function} updateRole Function which updates the role object. Default `_defaultUpdateRole`.
   * @for Roles
   * @private
   * @static
   */
  _forwardMigrate: function (updateUser, updateRole) {
    updateUser = updateUser || Roles._defaultUpdateUser;
    updateRole = updateRole || Roles._defaultUpdateRole;

    //drop old index, preventing mongo duplicate key 'null' errors
    Meteor.roles._dropIndex("name_1");

    Meteor.roles.find().forEach(function (role, index, cursor) {
      if (!Roles._isNewRole(role)) {
        updateRole(role, Roles._convertToNewRole(role));
      }
    });

    Meteor.users.find().forEach(function (user, index, cursor) {
      if (!Roles._isNewField(user.roles)) {
        updateUser(user, Roles._convertToNewField(user.roles));
      }
    });
  },

  /**
   * Migrates `Meteor.users` and `Meteor.roles` to the old format.
   *
   * We assume that we are converting back a failed migration, so values can only be
   * what were valid values in the old format. So no group names starting with `$` and
   * no subroles.
   *
   * @method _backwardMigrate
   * @param {Function} updateUser Function which updates the user object. Default `_defaultUpdateUser`.
   * @param {Function} updateRole Function which updates the role object. Default `_defaultUpdateRole`.
   * @param {Boolean} usingGroups Should we use groups or not.
   * @for Roles
   * @private
   * @static
   */
  _backwardMigrate: function (updateUser, updateRole, usingGroups) {
    updateUser = updateUser || Roles._defaultUpdateUser;
    updateRole = updateRole || Roles._defaultUpdateRole;

    Meteor.roles.find().forEach(function (role, index, cursor) {
      if (!Roles._isOldRole(role)) {
        updateRole(role, Roles._convertToOldRole(role));
      }
    });

    Meteor.users.find().forEach(function (user, index, cursor) {
      if (!Roles._isOldField(user.roles)) {
        updateUser(user, Roles._convertToOldField(user.roles, usingGroups));
      }
    });
  }
});
<|endoftext|>