<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var should = require('should');

var path = require('path');
var util = require('util');
var fs = require('fs')

var CLITest = require('../../../framework/arm-cli-test');
var testUtil = require('../../../util/util');
var testprefix = 'arm-cli-group-tests';

var groupPrefix = 'xplatTestGCreate';

var createdGroups = [];
var createdDeployments = [];

var requiredEnvironment = [
  { requiresToken: true },
  'AZURE_ARM_TEST_STORAGEACCOUNT',
  { name: 'AZURE_ARM_TEST_LOCATION', defaultValue: 'West US' }
];

var galleryTemplateName;
var galleryTemplateUrl;

describe('arm', function () {

  describe('group', function () {
    var suite;
    var testStorageAccount;
    var testLocation;
    var normalizedTestLocation;

    before(function (done) {
      suite = new CLITest(testprefix, requiredEnvironment);
      suite.setupSuite(done);
    });

    after(function (done) {
      suite.teardownSuite(done);
    });

    beforeEach(function (done) {
      suite.setupTest(function () {
        testStorageAccount = process.env.AZURE_ARM_TEST_STORAGEACCOUNT;
        testLocation = process.env.AZURE_ARM_TEST_LOCATION;
        normalizedTestLocation = testLocation.toLowerCase().replace(/ /g, '');
        testUtil.getTemplateInfo(suite, 'Microsoft.ASPNETStarterSite', function(error, templateInfo) {
          if (error) {
            return done(new Error('Could not get template info: ' + error));
          }
          galleryTemplateName = templateInfo.templateName;
          galleryTemplateUrl = templateInfo.templateUrl;
          done();
        });
      });
    });

    afterEach(function (done) {
      suite.teardownTest(done);
    });

    describe('create', function () {
      it('should create empty group', function (done) {
        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s --json', groupName, testLocation, function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group delete %s --json --quiet', groupName, function () {
              done();
            });
          });
        });
      });

      it('should create a group with a named deployment from a template file and a parameter file', function (done) {
        var parameterFile = path.join(__dirname, '../../../data/arm-deployment-parameters.json');
        var templateFile = path.join(__dirname, '../../../data/arm-deployment-template.json');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s -f %s -e %s -s %s -d %s --template-version %s --json',
          groupName, testLocation, templateFile, parameterFile, testStorageAccount, 'mydepTemplateFile', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group deployment list -g %s --json', groupName, function (listResult) {
              listResult.exitStatus.should.equal(0);

              var results = JSON.parse(listResult.text);
              results.length.should.be.above(0);

              suite.execute('group delete %s --json --quiet', groupName, function () {
                done();
              });
            });
          });
        });
      });

      it('should create a group with a named deployment from a gallery template and a parameter file', function (done) {
        var parameterFile = path.join(__dirname, '../../../data/startersite-parameters.json');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s -y %s -e %s -d %s --template-version %s --json',
          groupName, testLocation, galleryTemplateName, parameterFile, 'mydepGalleryTemplate', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group deployment list -g %s --json', groupName, function (listResult) {
              listResult.exitStatus.should.equal(0);

              var results = JSON.parse(listResult.text);
              results.length.should.be.above(0);

              suite.execute('group delete %s --json --quiet', groupName, function () {
                done();
              });
            });
          });
        });
      });

      it('should create a group with a named deployment from a template uri and parameter string', function (done) {
        var parameterString = fs.readFileSync(path.join(__dirname, '../../../data/startersite-parameters.json')).toString().replace(/\n/g, '').replace(/\r/g, '');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s --template-uri %s -p %s -d %s --template-version %s --json',
          groupName, testLocation, galleryTemplateUrl, parameterString, 'mydeptemplateUrl', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group deployment list -g %s --json', groupName, function (listResult) {
              listResult.exitStatus.should.equal(0);

              var results = JSON.parse(listResult.text);
              results.length.should.be.above(0);

              suite.execute('group delete %s --json --quiet', groupName, function () {
                done();
              });
            });
          });
        });
      });
    });

    describe('show', function () {
      it('should show information of an empty group', function (done) {
        var groupName = suite.generateId('xplatTestGrpShow', createdGroups, suite.isMocked);
        suite.execute('group create %s --location %s --json', groupName, testLocation, function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group show %s --json', groupName, function (showResult) {
            showResult.exitStatus.should.equal(0);

            var group = JSON.parse(showResult.text);
            group.name.should.equal(groupName);
            group.resources.length.should.equal(0);
            group.properties.provisioningState.should.equal('Succeeded');

            suite.execute('group delete %s --json --quiet', groupName, function () {
              done();
            });
          });
        });
      });

      it('should show information of a group with resources created in it', function (done) {
        var parameterFile = path.join(__dirname, '../../../data/arm-deployment-parameters.json');
        var templateFile = path.join(__dirname, '../../../data/arm-deployment-template.json');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s -f %s -e %s -s %s -d %s --template-version %s --json',
          groupName, testLocation, templateFile, parameterFile, testStorageAccount, 'mydepTemplateFile', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group show %s --json', groupName, function (showResult) {
            showResult.exitStatus.should.equal(0);

            var group = JSON.parse(showResult.text);
            group.name.should.equal(groupName);
            group.properties.provisioningState.should.equal('Succeeded');

            group.resources.forEach(function (item) {
              item.location.should.equal(testLocation);
              if (item.type === 'Microsoft.Web/serverFarms') {
                item.name.should.equal('xDeploymentTestHost1');
              }
              else if (item.type === 'Microsoft.Web/sites') {
                item.name.should.equal('xDeploymentTestSite1');
              }
            });

            suite.execute('group delete %s --json --quiet', groupName, function () {
              done();
            });
          });
        });
      });
    });

    describe('log show', function () {
      var groupName;
      var deploymentName;
      var deploymentName1;

      beforeEach(function (done) {
        if(!groupName) {
          setupForLogShow(done);
        } else {
          done();
        }
      });

      after(function (done) {
        if(!suite.isMocked || suite.isRecording) {
          cleanupForLogShow(done);
        } else {
          done();
        }
      });

      //create a group named xDeploymentTestGroup with two deployments 'Deploy1' and 'Deploy2'
      function setupForLogShow (done) {
        var parameterFile = path.join(__dirname, '../../../data/startersite-parameters.json');
        groupName = suite.generateId('xDeploymentTestGroup', createdGroups, suite.isMocked);
        deploymentName = suite.generateId('Deploy1', createdDeployments, suite.isMocked);
        var commandToCreateDeployment = util.format('group deployment create -f %s -g %s -n %s -e %s --json -vv',
            galleryTemplateUrl, groupName, deploymentName, parameterFile);

        console.log('  . Creating setup for running group log show tests');
        suite.execute('group create %s --location %s --json', groupName, testLocation, function (result) {
          result.exitStatus.should.equal(0);
          suite.execute(commandToCreateDeployment, function (result) {
            result.exitStatus.should.equal(0);
            poll(1, done);
          });
        });
      }

      //Polls for the output of group log show at an interval of 20 seconds for 3 times (max).
      function poll (counter, done) {
        suite.execute('group log show -n %s -l --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          counter = counter + 1;
          if (result.text === '' && counter <= 3) {
            setTimeout(function () { poll(counter, done); }, 20000);
          } else if (result.text === '' && counter >= 3) {
            throw new Error("group log show command is taking forever, bail out!!");
          } else {
            done();
          }
        });
      }

      function cleanupForLogShow (done) {
        suite.execute('group delete %s --json --quiet', groupName, function () {
          console.log('  . Performing cleanup of group log show tests')
          done();
        });
      }

      //Validates the content of Logs
      function validateLogContent (logs) {
        logs.forEach(function (item) {
          item.resourceGroupName.should.equal(groupName);
          item.status.value.should.not.match(/^Failed$/i);
        });
      }

      it('should return logs of all the operations', function (done) {
        suite.execute('group log show -n %s --all --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should return logs of the last deployment with the --last-deployment switch', function (done) {
        suite.execute('group log show -n %s --last-deployment --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should return logs of the last deployment by default', function (done) {
        suite.execute('group log show -n %s --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should return logs of the specified deployment', function (done) {
        suite.execute('group log show -n %s -d %s --json', groupName, deploymentName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should fail when an invalid resource group is provided', function (done) {
        suite.execute('group log show -n %s --json', 'random_group_name', function (result) {
          result.exitStatus.should.equal(1);
          result.errorText.should.include('Resource group \'random_group_name\' could not be found.');
          done();
        });
      });

      it('should fail when an invalid deployment name is provided', function (done) {
        suite.execute('group log show -n %s -d %s --json', groupName, 'random_deployment_name', function (result) {
          result.exitStatus.should.equal(1);
          result.errorText.should.include('Deployment \'random_deployment_name\' could not be found.');
          done();
        });
      });
    });
  });
});
<commit_msg>Remove invalid test assertion<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var should = require('should');

var path = require('path');
var util = require('util');
var fs = require('fs')

var CLITest = require('../../../framework/arm-cli-test');
var testUtil = require('../../../util/util');
var testprefix = 'arm-cli-group-tests';

var groupPrefix = 'xplatTestGCreate';

var createdGroups = [];
var createdDeployments = [];

var requiredEnvironment = [
  { requiresToken: true },
  'AZURE_ARM_TEST_STORAGEACCOUNT',
  { name: 'AZURE_ARM_TEST_LOCATION', defaultValue: 'West US' }
];

var galleryTemplateName;
var galleryTemplateUrl;

describe('arm', function () {

  describe('group', function () {
    var suite;
    var testStorageAccount;
    var testLocation;
    var normalizedTestLocation;

    before(function (done) {
      suite = new CLITest(testprefix, requiredEnvironment);
      suite.setupSuite(done);
    });

    after(function (done) {
      suite.teardownSuite(done);
    });

    beforeEach(function (done) {
      suite.setupTest(function () {
        testStorageAccount = process.env.AZURE_ARM_TEST_STORAGEACCOUNT;
        testLocation = process.env.AZURE_ARM_TEST_LOCATION;
        normalizedTestLocation = testLocation.toLowerCase().replace(/ /g, '');
        testUtil.getTemplateInfo(suite, 'Microsoft.ASPNETStarterSite', function(error, templateInfo) {
          if (error) {
            return done(new Error('Could not get template info: ' + error));
          }
          galleryTemplateName = templateInfo.templateName;
          galleryTemplateUrl = templateInfo.templateUrl;
          done();
        });
      });
    });

    afterEach(function (done) {
      suite.teardownTest(done);
    });

    describe('create', function () {
      it('should create empty group', function (done) {
        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s --json', groupName, testLocation, function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group delete %s --json --quiet', groupName, function () {
              done();
            });
          });
        });
      });

      it('should create a group with a named deployment from a template file and a parameter file', function (done) {
        var parameterFile = path.join(__dirname, '../../../data/arm-deployment-parameters.json');
        var templateFile = path.join(__dirname, '../../../data/arm-deployment-template.json');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s -f %s -e %s -s %s -d %s --template-version %s --json',
          groupName, testLocation, templateFile, parameterFile, testStorageAccount, 'mydepTemplateFile', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group deployment list -g %s --json', groupName, function (listResult) {
              listResult.exitStatus.should.equal(0);

              var results = JSON.parse(listResult.text);
              results.length.should.be.above(0);

              suite.execute('group delete %s --json --quiet', groupName, function () {
                done();
              });
            });
          });
        });
      });

      it('should create a group with a named deployment from a gallery template and a parameter file', function (done) {
        var parameterFile = path.join(__dirname, '../../../data/startersite-parameters.json');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s -y %s -e %s -d %s --template-version %s --json',
          groupName, testLocation, galleryTemplateName, parameterFile, 'mydepGalleryTemplate', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group deployment list -g %s --json', groupName, function (listResult) {
              listResult.exitStatus.should.equal(0);

              var results = JSON.parse(listResult.text);
              results.length.should.be.above(0);

              suite.execute('group delete %s --json --quiet', groupName, function () {
                done();
              });
            });
          });
        });
      });

      it('should create a group with a named deployment from a template uri and parameter string', function (done) {
        var parameterString = fs.readFileSync(path.join(__dirname, '../../../data/startersite-parameters.json')).toString().replace(/\n/g, '').replace(/\r/g, '');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s --template-uri %s -p %s -d %s --template-version %s --json',
          groupName, testLocation, galleryTemplateUrl, parameterString, 'mydeptemplateUrl', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group list --json', function (listResult) {
            listResult.exitStatus.should.equal(0);
            var groups = JSON.parse(listResult.text);

            groups.some(function (g) { return (g.name === groupName && g.location === normalizedTestLocation && g.provisioningState === 'Succeeded'); }).should.be.true;

            suite.execute('group deployment list -g %s --json', groupName, function (listResult) {
              listResult.exitStatus.should.equal(0);

              var results = JSON.parse(listResult.text);
              results.length.should.be.above(0);

              suite.execute('group delete %s --json --quiet', groupName, function () {
                done();
              });
            });
          });
        });
      });
    });

    describe('show', function () {
      it('should show information of an empty group', function (done) {
        var groupName = suite.generateId('xplatTestGrpShow', createdGroups, suite.isMocked);
        suite.execute('group create %s --location %s --json', groupName, testLocation, function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group show %s --json', groupName, function (showResult) {
            showResult.exitStatus.should.equal(0);

            var group = JSON.parse(showResult.text);
            group.name.should.equal(groupName);
            group.resources.length.should.equal(0);
            group.properties.provisioningState.should.equal('Succeeded');

            suite.execute('group delete %s --json --quiet', groupName, function () {
              done();
            });
          });
        });
      });

      it('should show information of a group with resources created in it', function (done) {
        var parameterFile = path.join(__dirname, '../../../data/arm-deployment-parameters.json');
        var templateFile = path.join(__dirname, '../../../data/arm-deployment-template.json');

        var groupName = suite.generateId(groupPrefix, createdGroups, suite.isMocked);

        suite.execute('group create %s --location %s -f %s -e %s -s %s -d %s --template-version %s --json',
          groupName, testLocation, templateFile, parameterFile, testStorageAccount, 'mydepTemplateFile', '1.0.0.0', function (result) {
          result.exitStatus.should.equal(0);

          suite.execute('group show %s --json', groupName, function (showResult) {
            showResult.exitStatus.should.equal(0);

            var group = JSON.parse(showResult.text);
            group.name.should.equal(groupName);
            group.properties.provisioningState.should.equal('Succeeded');

            group.resources.forEach(function (item) {
              item.location.should.equal(testLocation);
              if (item.type === 'Microsoft.Web/serverFarms') {
                item.name.should.equal('xDeploymentTestHost1');
              }
              else if (item.type === 'Microsoft.Web/sites') {
                item.name.should.equal('xDeploymentTestSite1');
              }
            });

            suite.execute('group delete %s --json --quiet', groupName, function () {
              done();
            });
          });
        });
      });
    });

    describe('log show', function () {
      var groupName;
      var deploymentName;
      var deploymentName1;

      beforeEach(function (done) {
        if(!groupName) {
          setupForLogShow(done);
        } else {
          done();
        }
      });

      after(function (done) {
        if(!suite.isMocked || suite.isRecording) {
          cleanupForLogShow(done);
        } else {
          done();
        }
      });

      //create a group named xDeploymentTestGroup with two deployments 'Deploy1' and 'Deploy2'
      function setupForLogShow (done) {
        var parameterFile = path.join(__dirname, '../../../data/startersite-parameters.json');
        groupName = suite.generateId('xDeploymentTestGroup', createdGroups, suite.isMocked);
        deploymentName = suite.generateId('Deploy1', createdDeployments, suite.isMocked);
        var commandToCreateDeployment = util.format('group deployment create -f %s -g %s -n %s -e %s --json -vv',
            galleryTemplateUrl, groupName, deploymentName, parameterFile);

        console.log('  . Creating setup for running group log show tests');
        suite.execute('group create %s --location %s --json', groupName, testLocation, function (result) {
          result.exitStatus.should.equal(0);
          suite.execute(commandToCreateDeployment, function (result) {
            result.exitStatus.should.equal(0);
            poll(1, done);
          });
        });
      }

      //Polls for the output of group log show at an interval of 20 seconds for 3 times (max).
      function poll (counter, done) {
        suite.execute('group log show -n %s -l --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          counter = counter + 1;
          if (result.text === '' && counter <= 3) {
            setTimeout(function () { poll(counter, done); }, 20000);
          } else if (result.text === '' && counter >= 3) {
            throw new Error("group log show command is taking forever, bail out!!");
          } else {
            done();
          }
        });
      }

      function cleanupForLogShow (done) {
        suite.execute('group delete %s --json --quiet', groupName, function () {
          console.log('  . Performing cleanup of group log show tests')
          done();
        });
      }

      //Validates the content of Logs
      function validateLogContent (logs) {
        logs.forEach(function (item) {
          item.resourceGroupName.should.equal(groupName);
          //item.status.value.should.not.match(/^Failed$/i);
        });
      }

      it('should return logs of all the operations', function (done) {
        suite.execute('group log show -n %s --all --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should return logs of the last deployment with the --last-deployment switch', function (done) {
        suite.execute('group log show -n %s --last-deployment --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should return logs of the last deployment by default', function (done) {
        suite.execute('group log show -n %s --json', groupName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should return logs of the specified deployment', function (done) {
        suite.execute('group log show -n %s -d %s --json', groupName, deploymentName, function (result) {
          result.exitStatus.should.equal(0);
          result.text.should.not.be.empty;
          validateLogContent(JSON.parse(result.text));
          done();
        });
      });

      it('should fail when an invalid resource group is provided', function (done) {
        suite.execute('group log show -n %s --json', 'random_group_name', function (result) {
          result.exitStatus.should.equal(1);
          result.errorText.should.include('Resource group \'random_group_name\' could not be found.');
          done();
        });
      });

      it('should fail when an invalid deployment name is provided', function (done) {
        suite.execute('group log show -n %s -d %s --json', groupName, 'random_deployment_name', function (result) {
          result.exitStatus.should.equal(1);
          result.errorText.should.include('Deployment \'random_deployment_name\' could not be found.');
          done();
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>(function (ng, app) {
    'use strict';

    app.directive('addUserButton', [
        '$http', '$modal', function ($http, $modal) {
            return {
                scope: {
                    userResult: '=?addUser',
                    selectResult: '=?forSelect2'
                },
                replace: true,
                templateUrl: 'partials/directives/add-user-button.html',
                link: function (scope, element, attr) {

                    scope.open = function () {
                        var modal = $modal.open({
                            backdrop: "static", //modal can't be closed by clicking outside modal
                            templateUrl: 'partials/directives/add-user-modal.html',
                            resolve: {
                                user: ['userService', function (userService) {
                                    return userService.getUser();
                                }]
                            },
                            controller: [
                                '$scope', 'notify', '$modalInstance', 'user', function ($scope, notify, $modalInstance, user) {

                                    $scope.newUser = {};

                                    $scope.addUser = function () {

                                        if ($scope.newUser.email != $scope.newUser.repeatEmail) {
                                            notify.addErrorMessage("Email addresserne er ikke ens.");
                                        }

                                        if ($scope.newUser.form.$invalid) return;

                                        var name = $scope.newUser.name;
                                        var email = $scope.newUser.email;

                                        var data = {
                                            "name": name,
                                            "email": email,
                                            "createdInId": user.currentOrganizationId
                                        };

                                        var msg = notify.addInfoMessage("Arbejder ...", false);

                                        $http.post("api/user", data, { handleBusy: true }).success(function (result) {
                                            msg.toSuccessMessage(name + " er oprettet i KITOS");

                                            $modalInstance.close(result.response);
                                        }).error(function (result) {
                                            msg.toErrorMessage("Fejl! " + name + " blev ikke oprettet i KITOS!");
                                        });
                                    };

                                    $scope.cancel = function () {
                                        $modalInstance.dismiss('cancel');
                                    };
                                }
                            ]
                        });

                        modal.result.then(function (userResult) {
                            scope.userResult = userResult;

                            scope.selectResult = {
                                id: userResult.id,
                                text: userResult.name
                            };
                        }, function () {
                            scope.userResult = null;
                            scope.selectResult = null;
                        });
                    };

                }
            };
        }
    ]);

    app.directive('selectUser', [
        '$rootScope', '$http', '$timeout', function ($rootScope, $http, $timeout) {

            //format of dropdown options
            function formatResult(obj) {
                var result = "
" + obj.text + "
";

                //obj.user might contain more info about the user
                if (obj.user) {
                    result += "
" + obj.user.email;

                    if (obj.user.defaultOrganizationUnitName)
                        result += ", " + obj.user.defaultOrganizationUnitName;

                    result += "
";
                }

                return result;
            }

            //format of the selected user
            function formatSelection(obj) {
                return obj.text;
            }

            return {
                scope: {
                    inputName: '@?name',
                    userModel: '=',
                    addUser: '@?',
                    allowClear: '@?',
                    onSelect: '&?',
                    ngDisabled: '=?'
                },
                replace: true,
                templateUrl: 'partials/directives/select-user.html',
                controller: [
                    '$scope', function ($scope) {
                        $scope.onChange = function () {

                            //for some reason (probably a bugger in select2)
                            //this is called 2 times, once with the original select value (like 1 or "")
                            //and once with the object value of select2 {id, text}.
                            //we only need the last one
                            if (typeof $scope.userModel !== 'object') return;

                            //timeout, otherwise we get the bad version of the model.
                            $timeout($scope.onSelect);
                        };

                        $scope.selectUserOptions = {

                            //don't escape markup, otherwise formatResult will be bugged
                            escapeMarkup: function (m) { return m; },
                            formatResult: formatResult,
                            formatSelection: formatSelection,

                            allowClear: !!$scope.allowClear,

                            minimumInputLength: 1,
                            initSelection: function (elem, callback) {
                            },
                            ajax: {
                                data: function (term, page) {
                                    return { query: term };
                                },
                                quietMillis: 500,
                                transport: function (queryParams) {
                                    var res = $http.get('api/user?q=' + queryParams.data.query).then(queryParams.success);
                                    res.abort = function () {
                                        return null;
                                    };

                                    return res;
                                },

                                results: function (data, page) {
                                    var results = [];

                                    _.each(data.data.response, function (user) {

                                        results.push({
                                            id: user.id, //select2 mandatory
                                            text: user.name, //select2 mandatory
                                            user: user //not mandatory, for extra info when formatting
                                        });
                                    });

                                    return { results: results };
                                }
                            }
                        };
                    }
                ]
            };
        }
    ]);

    app.directive('selectAccessModifier', [
        function () {
            return {
                priority: 1,
                replace: true,
                templateUrl: 'partials/directives/select-access-modifier.html'
            };
        }
    ]);

    app.directive('selectStatus2', ['$timeout',
        function ($timeout) {
            return {
                scope: {
                    canWrite: '=',
                },
                require: 'ngModel',
                templateUrl: 'partials/directives/select-status2.html',

                link: function (scope, element, attr, ngModel) {
                    scope.setModel = function (n) {
                        //only update on change
                        if (scope.model == n) return;

                        //save new value
                        scope.model = n;

                        $timeout(function () {
                            //then trigger event
                            ngModel.$setViewValue(scope.model);

                            //this triggers the autosave directive
                            element.triggerHandler("blur");
                        });
                    };

                    //read value from ngModel
                    ngModel.$render = function () {
                        scope.model = ngModel.$viewValue;
                    };
                }
            };
        }
    ]);

    app.directive('showStatus', [
        '$timeout', function ($timeout) {
            return {
                scope: {
                    status: '=showStatus'
                },
                replace: false,
                templateUrl: 'partials/directives/show-status.html',

                link: function (scope, element, attr) {
                    scope.ready = false;
                    update();

                    function update() {
                        $timeout(function () {
                            if (!scope.status) {
                                update();
                                return;
                            }
                            scope.ready = true;
                        });
                    }
                    scope.$watch("status", function (newval, oldval) {
                        if (newval === oldval) return;

                        update();
                    });
                }
            };
        }
    ]);

    app.directive('dateToString', ['$timeout', 'dateFilter', function($timeout, dateFilter) {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, element, attr, ctrl) {
                ctrl.$parsers.push(function (value) {
                    if (value instanceof Date)
                        return dateFilter(value, 'yyyy-MM-dd');
                    return value;
                });
            }
        };
    }]);

    app.directive('autosave', [
        '$http', '$timeout', 'notify', function ($http, $timeout, notify) {
            return {
                restrict: 'A',
                require: 'ngModel',
                priority: 0,
                link: function (scope, element, attrs, ctrl) {
                    var oldValue;
                    $timeout(function () {
                        oldValue = ctrl.$modelValue; // get initial value
                    });

                    function saveIfNew() {
                        var newValue = ctrl.$modelValue;
                        if (attrs.pluck)
                            newValue = _.pluck(newValue, attrs.pluck);

                        var payload = {};
                        payload[attrs.field] = newValue;

                        if (newValue !== oldValue) {
                            save(payload);
                        }
                    }

                    function saveCheckbox() {
                        // ctrl.$viewValue reflects the old state.
                        // using timeout to wait for the value to update
                        $timeout(function() {
                            var newValue = ctrl.$modelValue;
                            var payload = {};
                            payload[attrs.field] = newValue;
                            save(payload);
                        });
                    }

                    function saveSelect2() {
                        // ctrl.$viewValue reflects the old state.
                        // using timeout to wait for the value to update
                        $timeout(function () {
                            var newValue;

                            var viewValue = ctrl.$viewValue;
                            if (angular.isArray(viewValue)) {
                                newValue = _.pluck(viewValue, 'id');
                            } else if (angular.isObject(viewValue)) {
                                newValue = viewValue.id;
                            } else {
                                newValue = viewValue;
                            }

                            var payload = {};
                            payload[attrs.field] = newValue;

                            save(payload);
                        });
                    }

                    function save(payload) {
                        var msg = notify.addInfoMessage("Gemmer...", false);
                        $http({ method: 'PATCH', url: attrs.autosave, data: payload })
                            .success(function () {
                                msg.toSuccessMessage("Feltet er opdateret.");
                                oldValue = ctrl.$modelValue;
                            })
                            .error(function () {
                                msg.toErrorMessage("Fejl! Feltet kunne ikke ændres!");
                            });
                    }

                    // type=hidden is cause select2 fields are usually hidden and trigger the change event
                    if (attrs.type === "hidden" || !angular.isUndefined(attrs.uiSelect2)) {
                        element.bind('change', saveSelect2);
                    } else if (attrs.type === "checkbox") {
                        element.bind('change', saveCheckbox);
                    } else {
                        element.bind('blur', saveIfNew);
                    }
                }
            };
        }
    ]);

    app.directive('datewriter', ['$timeout', function ($timeout) {
        return {
            scope: {
                'isDisabled': '=?isDisabled'
            },
            templateUrl: 'partials/directives/datewriter.html',
            require: 'ngModel',
            link: function (scope, element, attr, ctrl) {

                scope.date = {};

                function read() {
                    var parsedDate = moment(ctrl.$modelValue);

                    scope.date.dateStr = parsedDate.isValid() ? moment(ctrl.$modelValue).format("DD-MM-YY", "da", true) : "dd-mm-åå";
                }

                read();
                ctrl.$render = read;

                function write() {
                    scope.dateInvalid = false;

                    var newDate = moment(scope.date.dateStr, "DD-MM-YY", "da", true);
                    if (!newDate.isValid()) {
                        scope.dateInvalid = true;
                        return;
                    }

                    ctrl.$setViewValue(newDate.format("YYYY-MM-DD HH:mm:ss"));

                    $timeout(function () {
                        //this triggers the autosave directive
                        element.triggerHandler("blur");
                    });
                }

                scope.write = write;
            }
        };
    }]);

    app.directive('datereader', [function () {
        return {
            scope: true,
            template: '{{dateStr}}',
            require: 'ngModel',
            link: function (scope, element, attr, ctrl) {

                scope.date = {};

                function read() {
                    if (angular.isUndefined(ctrl.$modelValue) || ctrl.$modelValue == null) scope.dateStr = "";
                    else scope.dateStr = moment(ctrl.$modelValue).format("DD-MM-YY", "da", true);
                }

                read();
                ctrl.$render = read;
            }
        };
    }]);

    app.directive('simpleComment', [function () {
        return {
            scope: true,
            require: 'ngModel',
            template: '<button class="btn btn-link btn-sm" data-ng-disabled="disabled" data-popover="{{comment}}"></button>',
            link: function (scope, element, attr, ctrl) {

                function setDisabled(disabled) {
                    scope.disabled = disabled;
                    scope.ngClassObj = { 'faded': disabled };
                }

                setDisabled(true);

                ctrl.$render = function () {
                    setDisabled(!ctrl.$viewValue);

                    scope.comment = ctrl.$viewValue;
                };
            }
        };
    }]);

    app.directive('selectOrgUnit', ['$http', '$timeout', '$sce', 'userService',
        function ($http, $timeout, $sce, userService) {
            return {
                scope: {
                    extraOptions: '=?',
                    allowClear: '@?',
                    ngDisabled: '=?'
                },
                //we require ngModel to be available on the outer tag
                require: 'ngModel',
                priority: 0,
                templateUrl: 'partials/directives/select-org-unit.html',
                link: function (scope, element, attrs, ctrl) {
                    //this is called when the user selects something from select2
                    element.bind('change', function () {
                        $timeout(function () {
                            //update the view value
                            ctrl.$setViewValue(scope.select.selected);

                            //this triggers the autosave directive
                            element.triggerHandler("blur");
                        });
                    });

                    //when the outer ngModel is changed, update the inner model
                    ctrl.$render = function () {
                        scope.select.selected = ctrl.$viewValue;
                    };

                    //stores the <options> for the select
                    var options = [];

                    //settings for select2
                    var settings = {
                        allowClear: !!scope.allowClear,

                        //don't format markup in result
                        escapeMarkup: function (m) { return m; },

                        //when an option has been selected, print the no-html version
                        formatSelection: function (item) {

                            var option;
                            if (item.id) {
                                option = _.findWhere(options, { id: parseInt(item.id) });
                            } else {
                                option = _.findWhere(options, { id: parseInt(item) });
                            }

                            if (option) {
                                return option.selectedText;
                            } else return null;
                        }
                    };

                    if (scope.extraOptions) {
                        _.each(scope.extraOptions, function (extraOption) {
                            var option = {
                                id: extraOption.id,
                                text: extraOption.text,
                                selectedText: extraOption.text
                            };
                            options.push(option);
                        });
                    }

                    //loads the org unit roots
                    userService.getUser().then(function (user) {

                        $http.get('api/organizationUnit?organization=' + user.currentOrganizationId, { cache: true }).success(function (result) {

                            //recursive function for added indentation,
                            //and pushing org units to the list in the right order (depth-first)
                            function visit(orgUnit, indentation) {
                                var option = {
                                    id: orgUnit.id,
                                    text: $sce.trustAsHtml(indentation + orgUnit.name),
                                    selectedText: orgUnit.name
                                };

                                options.push(option);

                                _.each(orgUnit.children, function (child) {
                                    //indentation is non breaking spaces (alt+255)
                                    return visit(child, indentation + "    ");
                                });
                            }

                            visit(result.response, "");

                            scope.select.isReady = true;
                        });

                    });

                    scope.select = {
                        settings: settings,
                        options: options,
                        isReady: false,
                        selected: null
                    };
                }

            };
        }

    ]);

    app.directive('suggestNew', ['$http', 'notify', function ($http, notify) {
        return {
            scope: {
                url: '@'
            },
            templateUrl: 'partials/local-config/suggest-new.html',
            link: function (scope, element, attrs) {
                scope.suggest = function () {
                    var data = {
                        "isSuggestion": true,
                        "name": scope.suggestion
                    };

                    $http.post(scope.url, data).success(function (result) {
                        notify.addSuccessMessage('Foreslag sendt!');
                        scope.suggestion = "";
                    }).error(function (result) {
                        notify.addErrorMessage('Kunne ikke sende foreslag!');
                    });
                };
            }
        };
    }]);

    app.directive('suggestNewRole', ['$http', 'notify', function ($http, notify) {
        return {
            scope: {
                url: '@'
            },
            templateUrl: 'partials/local-config/suggest-new-role.html',
            link: function (scope, element, attrs) {
                scope.suggest = function () {

                    var data = {
                        "isSuggestion": true,
                        "name": scope.suggestion,
                        "hasReadAccess": true,
                        "hasWriteAccess": scope.writeAccess
                    };

                    $http.post(scope.url, data).success(function (result) {
                        notify.addSuccessMessage('Foreslag sendt!');
                        scope.suggestion = "";
                    }).error(function (result) {
                        notify.addErrorMessage('Kunne ikke sende foreslag!');
                    });
                };
            }
        };
    }]);

    app.directive('optionList', ['$http', function ($http) {
        return {
            scope: {
                optionsUrl: '@',
                title: '@',
            },
            templateUrl: 'partials/local-config/optionlist.html',
            link: function (scope, element, attrs) {

                scope.list = [];

                $http.get(scope.optionsUrl).success(function (result) {
                    _.each(result.response, function (v) {
                        scope.list.push({
                            id: v.id,
                            name: v.name,
                            note: v.note
                        });
                    });
                });
            }
        };
    }]);

})(angular, app);<commit_msg>Removed datewriter directive<commit_after>(function (ng, app) {
    'use strict';

    app.directive('addUserButton', [
        '$http', '$modal', function ($http, $modal) {
            return {
                scope: {
                    userResult: '=?addUser',
                    selectResult: '=?forSelect2'
                },
                replace: true,
                templateUrl: 'partials/directives/add-user-button.html',
                link: function (scope, element, attr) {

                    scope.open = function () {
                        var modal = $modal.open({
                            backdrop: "static", //modal can't be closed by clicking outside modal
                            templateUrl: 'partials/directives/add-user-modal.html',
                            resolve: {
                                user: ['userService', function (userService) {
                                    return userService.getUser();
                                }]
                            },
                            controller: [
                                '$scope', 'notify', '$modalInstance', 'user', function ($scope, notify, $modalInstance, user) {

                                    $scope.newUser = {};

                                    $scope.addUser = function () {

                                        if ($scope.newUser.email != $scope.newUser.repeatEmail) {
                                            notify.addErrorMessage("Email addresserne er ikke ens.");
                                        }

                                        if ($scope.newUser.form.$invalid) return;

                                        var name = $scope.newUser.name;
                                        var email = $scope.newUser.email;

                                        var data = {
                                            "name": name,
                                            "email": email,
                                            "createdInId": user.currentOrganizationId
                                        };

                                        var msg = notify.addInfoMessage("Arbejder ...", false);

                                        $http.post("api/user", data, { handleBusy: true }).success(function (result) {
                                            msg.toSuccessMessage(name + " er oprettet i KITOS");

                                            $modalInstance.close(result.response);
                                        }).error(function (result) {
                                            msg.toErrorMessage("Fejl! " + name + " blev ikke oprettet i KITOS!");
                                        });
                                    };

                                    $scope.cancel = function () {
                                        $modalInstance.dismiss('cancel');
                                    };
                                }
                            ]
                        });

                        modal.result.then(function (userResult) {
                            scope.userResult = userResult;

                            scope.selectResult = {
                                id: userResult.id,
                                text: userResult.name
                            };
                        }, function () {
                            scope.userResult = null;
                            scope.selectResult = null;
                        });
                    };

                }
            };
        }
    ]);

    app.directive('selectUser', [
        '$rootScope', '$http', '$timeout', function ($rootScope, $http, $timeout) {

            //format of dropdown options
            function formatResult(obj) {
                var result = "
" + obj.text + "
";

                //obj.user might contain more info about the user
                if (obj.user) {
                    result += "
" + obj.user.email;

                    if (obj.user.defaultOrganizationUnitName)
                        result += ", " + obj.user.defaultOrganizationUnitName;

                    result += "
";
                }

                return result;
            }

            //format of the selected user
            function formatSelection(obj) {
                return obj.text;
            }

            return {
                scope: {
                    inputName: '@?name',
                    userModel: '=',
                    addUser: '@?',
                    allowClear: '@?',
                    onSelect: '&?',
                    ngDisabled: '=?'
                },
                replace: true,
                templateUrl: 'partials/directives/select-user.html',
                controller: [
                    '$scope', function ($scope) {
                        $scope.onChange = function () {

                            //for some reason (probably a bugger in select2)
                            //this is called 2 times, once with the original select value (like 1 or "")
                            //and once with the object value of select2 {id, text}.
                            //we only need the last one
                            if (typeof $scope.userModel !== 'object') return;

                            //timeout, otherwise we get the bad version of the model.
                            $timeout($scope.onSelect);
                        };

                        $scope.selectUserOptions = {

                            //don't escape markup, otherwise formatResult will be bugged
                            escapeMarkup: function (m) { return m; },
                            formatResult: formatResult,
                            formatSelection: formatSelection,

                            allowClear: !!$scope.allowClear,

                            minimumInputLength: 1,
                            initSelection: function (elem, callback) {
                            },
                            ajax: {
                                data: function (term, page) {
                                    return { query: term };
                                },
                                quietMillis: 500,
                                transport: function (queryParams) {
                                    var res = $http.get('api/user?q=' + queryParams.data.query).then(queryParams.success);
                                    res.abort = function () {
                                        return null;
                                    };

                                    return res;
                                },

                                results: function (data, page) {
                                    var results = [];

                                    _.each(data.data.response, function (user) {

                                        results.push({
                                            id: user.id, //select2 mandatory
                                            text: user.name, //select2 mandatory
                                            user: user //not mandatory, for extra info when formatting
                                        });
                                    });

                                    return { results: results };
                                }
                            }
                        };
                    }
                ]
            };
        }
    ]);

    app.directive('selectAccessModifier', [
        function () {
            return {
                priority: 1,
                replace: true,
                templateUrl: 'partials/directives/select-access-modifier.html'
            };
        }
    ]);

    app.directive('selectStatus2', ['$timeout',
        function ($timeout) {
            return {
                scope: {
                    canWrite: '=',
                },
                require: 'ngModel',
                templateUrl: 'partials/directives/select-status2.html',

                link: function (scope, element, attr, ngModel) {
                    scope.setModel = function (n) {
                        //only update on change
                        if (scope.model == n) return;

                        //save new value
                        scope.model = n;

                        $timeout(function () {
                            //then trigger event
                            ngModel.$setViewValue(scope.model);

                            //this triggers the autosave directive
                            element.triggerHandler("blur");
                        });
                    };

                    //read value from ngModel
                    ngModel.$render = function () {
                        scope.model = ngModel.$viewValue;
                    };
                }
            };
        }
    ]);

    app.directive('showStatus', [
        '$timeout', function ($timeout) {
            return {
                scope: {
                    status: '=showStatus'
                },
                replace: false,
                templateUrl: 'partials/directives/show-status.html',

                link: function (scope, element, attr) {
                    scope.ready = false;
                    update();

                    function update() {
                        $timeout(function () {
                            if (!scope.status) {
                                update();
                                return;
                            }
                            scope.ready = true;
                        });
                    }
                    scope.$watch("status", function (newval, oldval) {
                        if (newval === oldval) return;

                        update();
                    });
                }
            };
        }
    ]);

    app.directive('dateToString', ['$timeout', 'dateFilter', function($timeout, dateFilter) {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, element, attr, ctrl) {
                ctrl.$parsers.push(function (value) {
                    if (value instanceof Date)
                        return dateFilter(value, 'yyyy-MM-dd');
                    return value;
                });
            }
        };
    }]);

    app.directive('autosave', [
        '$http', '$timeout', 'notify', function ($http, $timeout, notify) {
            return {
                restrict: 'A',
                require: 'ngModel',
                priority: 0,
                link: function (scope, element, attrs, ctrl) {
                    var oldValue;
                    $timeout(function () {
                        oldValue = ctrl.$modelValue; // get initial value
                    });

                    function saveIfNew() {
                        var newValue = ctrl.$modelValue;
                        if (attrs.pluck)
                            newValue = _.pluck(newValue, attrs.pluck);

                        var payload = {};
                        payload[attrs.field] = newValue;

                        if (newValue !== oldValue) {
                            save(payload);
                        }
                    }

                    function saveCheckbox() {
                        // ctrl.$viewValue reflects the old state.
                        // using timeout to wait for the value to update
                        $timeout(function() {
                            var newValue = ctrl.$modelValue;
                            var payload = {};
                            payload[attrs.field] = newValue;
                            save(payload);
                        });
                    }

                    function saveSelect2() {
                        // ctrl.$viewValue reflects the old state.
                        // using timeout to wait for the value to update
                        $timeout(function () {
                            var newValue;

                            var viewValue = ctrl.$viewValue;
                            if (angular.isArray(viewValue)) {
                                newValue = _.pluck(viewValue, 'id');
                            } else if (angular.isObject(viewValue)) {
                                newValue = viewValue.id;
                            } else {
                                newValue = viewValue;
                            }

                            var payload = {};
                            payload[attrs.field] = newValue;

                            save(payload);
                        });
                    }

                    function save(payload) {
                        var msg = notify.addInfoMessage("Gemmer...", false);
                        $http({ method: 'PATCH', url: attrs.autosave, data: payload })
                            .success(function () {
                                msg.toSuccessMessage("Feltet er opdateret.");
                                oldValue = ctrl.$modelValue;
                            })
                            .error(function () {
                                msg.toErrorMessage("Fejl! Feltet kunne ikke ændres!");
                            });
                    }

                    // type=hidden is cause select2 fields are usually hidden and trigger the change event
                    if (attrs.type === "hidden" || !angular.isUndefined(attrs.uiSelect2)) {
                        element.bind('change', saveSelect2);
                    } else if (attrs.type === "checkbox") {
                        element.bind('change', saveCheckbox);
                    } else {
                        element.bind('blur', saveIfNew);
                    }
                }
            };
        }
    ]);

    app.directive('datereader', [function () {
        return {
            scope: true,
            template: '{{dateStr}}',
            require: 'ngModel',
            link: function (scope, element, attr, ctrl) {

                scope.date = {};

                function read() {
                    if (angular.isUndefined(ctrl.$modelValue) || ctrl.$modelValue == null) scope.dateStr = "";
                    else scope.dateStr = moment(ctrl.$modelValue).format("DD-MM-YY", "da", true);
                }

                read();
                ctrl.$render = read;
            }
        };
    }]);

    app.directive('simpleComment', [function () {
        return {
            scope: true,
            require: 'ngModel',
            template: '<button class="btn btn-link btn-sm" data-ng-disabled="disabled" data-popover="{{comment}}"></button>',
            link: function (scope, element, attr, ctrl) {

                function setDisabled(disabled) {
                    scope.disabled = disabled;
                    scope.ngClassObj = { 'faded': disabled };
                }

                setDisabled(true);

                ctrl.$render = function () {
                    setDisabled(!ctrl.$viewValue);

                    scope.comment = ctrl.$viewValue;
                };
            }
        };
    }]);

    app.directive('selectOrgUnit', ['$http', '$timeout', '$sce', 'userService',
        function ($http, $timeout, $sce, userService) {
            return {
                scope: {
                    extraOptions: '=?',
                    allowClear: '@?',
                    ngDisabled: '=?'
                },
                //we require ngModel to be available on the outer tag
                require: 'ngModel',
                priority: 0,
                templateUrl: 'partials/directives/select-org-unit.html',
                link: function (scope, element, attrs, ctrl) {
                    //this is called when the user selects something from select2
                    element.bind('change', function () {
                        $timeout(function () {
                            //update the view value
                            ctrl.$setViewValue(scope.select.selected);

                            //this triggers the autosave directive
                            element.triggerHandler("blur");
                        });
                    });

                    //when the outer ngModel is changed, update the inner model
                    ctrl.$render = function () {
                        scope.select.selected = ctrl.$viewValue;
                    };

                    //stores the <options> for the select
                    var options = [];

                    //settings for select2
                    var settings = {
                        allowClear: !!scope.allowClear,

                        //don't format markup in result
                        escapeMarkup: function (m) { return m; },

                        //when an option has been selected, print the no-html version
                        formatSelection: function (item) {

                            var option;
                            if (item.id) {
                                option = _.findWhere(options, { id: parseInt(item.id) });
                            } else {
                                option = _.findWhere(options, { id: parseInt(item) });
                            }

                            if (option) {
                                return option.selectedText;
                            } else return null;
                        }
                    };

                    if (scope.extraOptions) {
                        _.each(scope.extraOptions, function (extraOption) {
                            var option = {
                                id: extraOption.id,
                                text: extraOption.text,
                                selectedText: extraOption.text
                            };
                            options.push(option);
                        });
                    }

                    //loads the org unit roots
                    userService.getUser().then(function (user) {

                        $http.get('api/organizationUnit?organization=' + user.currentOrganizationId, { cache: true }).success(function (result) {

                            //recursive function for added indentation,
                            //and pushing org units to the list in the right order (depth-first)
                            function visit(orgUnit, indentation) {
                                var option = {
                                    id: orgUnit.id,
                                    text: $sce.trustAsHtml(indentation + orgUnit.name),
                                    selectedText: orgUnit.name
                                };

                                options.push(option);

                                _.each(orgUnit.children, function (child) {
                                    //indentation is non breaking spaces (alt+255)
                                    return visit(child, indentation + "    ");
                                });
                            }

                            visit(result.response, "");

                            scope.select.isReady = true;
                        });

                    });

                    scope.select = {
                        settings: settings,
                        options: options,
                        isReady: false,
                        selected: null
                    };
                }

            };
        }

    ]);

    app.directive('suggestNew', ['$http', 'notify', function ($http, notify) {
        return {
            scope: {
                url: '@'
            },
            templateUrl: 'partials/local-config/suggest-new.html',
            link: function (scope, element, attrs) {
                scope.suggest = function () {
                    var data = {
                        "isSuggestion": true,
                        "name": scope.suggestion
                    };

                    $http.post(scope.url, data).success(function (result) {
                        notify.addSuccessMessage('Foreslag sendt!');
                        scope.suggestion = "";
                    }).error(function (result) {
                        notify.addErrorMessage('Kunne ikke sende foreslag!');
                    });
                };
            }
        };
    }]);

    app.directive('suggestNewRole', ['$http', 'notify', function ($http, notify) {
        return {
            scope: {
                url: '@'
            },
            templateUrl: 'partials/local-config/suggest-new-role.html',
            link: function (scope, element, attrs) {
                scope.suggest = function () {

                    var data = {
                        "isSuggestion": true,
                        "name": scope.suggestion,
                        "hasReadAccess": true,
                        "hasWriteAccess": scope.writeAccess
                    };

                    $http.post(scope.url, data).success(function (result) {
                        notify.addSuccessMessage('Foreslag sendt!');
                        scope.suggestion = "";
                    }).error(function (result) {
                        notify.addErrorMessage('Kunne ikke sende foreslag!');
                    });
                };
            }
        };
    }]);

    app.directive('optionList', ['$http', function ($http) {
        return {
            scope: {
                optionsUrl: '@',
                title: '@',
            },
            templateUrl: 'partials/local-config/optionlist.html',
            link: function (scope, element, attrs) {

                scope.list = [];

                $http.get(scope.optionsUrl).success(function (result) {
                    _.each(result.response, function (v) {
                        scope.list.push({
                            id: v.id,
                            name: v.name,
                            note: v.note
                        });
                    });
                });
            }
        };
    }]);

})(angular, app);<|endoftext|>


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

    return {
        restrict: 'E',
        scope: {},
        controller: function($scope) {
            $scope.userClients = {};
            Restangular
                .all('client')
                .getList()
                .then(clients => {
                    $scope.userClients = clients.data.map(client=>{return {label: client.name, value: client.id}});
                });
        },
        template: '<select multiple class="form-control ui-select-search ng-pristine ng-valid ng-touched" style="outline:0;" ng-model="::field" ng-options="client.label for client in userClients track by client.value"></select>'
    };
}

clientSummary.$inject = ['Restangular'];

export default clientSummary;<commit_msg>added a custom directive for clients.<commit_after>function clientSummary(Restangular) {
    'use strict';

    return {
        restrict: 'E',
        scope: {},
        controller: function($scope) {
            $scope.userClients = {};
            Restangular
                .all('client')
                .getList()
                .then(clients => {
                    $scope.userClients = clients.data.map(client=>{return {label: client.name, value: client.id}});
                });
        },
        template: '<select multiple class="form-control ui-select-search ng-pristine ng-valid ng-touched" style="outline:0;" field="::field" ng-options="client.label for client in userClients track by client.value"></select>'
    };
}

clientSummary.$inject = ['Restangular'];

export default clientSummary;<|endoftext|>


<commit_before>import React from 'react'
import { reduxForm } from 'redux-form'
import { createTodo, updateTodo, focusTodo } from '../actions'

const fields = [
  'id',
  'title',
  'due_date',
  'color',
  'marked',
]

class _TodoInlineForm extends React.Component {

  componentDidMount() {
    this.refs.titleInput.focus();
  }

  _handleSubmit(values) {
    return new Promise((resolve, reject) => {
      if (!values.title) {
        reject({title: 'Please input a title.', _error: "Submit validation failed."});
        return;
      }
      this.props.dispatch(this._submitAction.bind(this)(
        values,
        resolve,
        (e) => { alert(e.message); reject();}));
    });
  }

  render() {
    const { fields: {id, title, due_date, color, marked}, handleSubmit, submitting } = this.props
    return(
      <form className="todo-inline-form"
        onSubmit={handleSubmit(values => this._handleSubmit.bind(this)(values))}
        onBlur={this._onBlur.bind(this)}
        >

          <input
            ref="titleInput"
            className={"new-todo" + (title.error ? " error" : "")}
            placeholder={title.error || "What needs to be done?"}
            disabled={submitting}
            {...title}
          />

      </form>
    )
  }
}

@reduxForm({
  form: 'TodoInlineCreateForm',
  fields
})
export class TodoInlineCreateForm extends _TodoInlineForm {
  _submitAction(values, resolve, reject) {
    const { resetForm } = this.props;
    const titleInput = this.refs.titleInput;
    return createTodo({
      data: values,
      resolve: () => { resetForm(); titleInput.focus(); resolve(); },
      reject
    });
  }

  _onBlur() {}
}

@reduxForm({
  form: 'TodoInlineUpdateForm',
  fields
})
export class TodoInlineUpdateForm extends _TodoInlineForm {
  _submitAction(values, resolve, reject) {
    const { resetForm, dispatch } = this.props;
    return updateTodo({
      id: values.id,
      data: values,
      resolve: () => { resetForm(); dispatch(focusTodo(null)); resolve(); },
      reject
    });
  }
  _onBlur() {
    const { dispatch } = this.props;
    dispatch(focusTodo(null));
  }
}
<commit_msg>Fix error handling for todo inline form<commit_after>import React from 'react'
import { reduxForm } from 'redux-form'
import { createTodo, updateTodo, focusTodo } from '../actions'

const fields = [
  'id',
  'title',
  'due_date',
  'color',
  'marked',
]

class _TodoInlineForm extends React.Component {

  componentDidMount() {
    this.refs.titleInput.focus();
  }

  _handleSubmit(values) {
    return new Promise((resolve, reject) => {
      if (!values.title) {
        reject({title: 'Please input a title.', _error: "Submit validation failed."});
        return;
      }
      this.props.dispatch(this._submitAction.bind(this)(
        values,
        resolve,
        (e) => { alert(e._error); reject();}));
    });
  }

  render() {
    const { fields: {id, title, due_date, color, marked}, handleSubmit, submitting } = this.props
    return(
      <form className="todo-inline-form"
        onSubmit={handleSubmit(values => this._handleSubmit.bind(this)(values))}
        onBlur={this._onBlur.bind(this)}
        >

          <input
            ref="titleInput"
            className={"new-todo" + (title.error ? " error" : "")}
            placeholder={title.error || "What needs to be done?"}
            disabled={submitting}
            {...title}
          />

      </form>
    )
  }
}

@reduxForm({
  form: 'TodoInlineCreateForm',
  fields
})
export class TodoInlineCreateForm extends _TodoInlineForm {
  _submitAction(values, resolve, reject) {
    const { resetForm } = this.props;
    const titleInput = this.refs.titleInput;
    return createTodo({
      data: values,
      resolve: () => { resetForm(); titleInput.focus(); resolve(); },
      reject
    });
  }

  _onBlur() {}
}

@reduxForm({
  form: 'TodoInlineUpdateForm',
  fields
})
export class TodoInlineUpdateForm extends _TodoInlineForm {
  _submitAction(values, resolve, reject) {
    const { resetForm, dispatch } = this.props;
    return updateTodo({
      id: values.id,
      data: values,
      resolve: () => { resetForm(); dispatch(focusTodo(null)); resolve(); },
      reject
    });
  }
  _onBlur() {
    const { dispatch } = this.props;
    dispatch(focusTodo(null));
  }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = function (element) {

  this.el = element;
  this.init();

};

module.exports.prototype = {

  init: function () {

    console.log('Init header');
    this.setVariables();
    this.addEventListeners();
    this.checkCollapsed();

  },

  setVariables: function () {

    this.navigationToggle = this.el.querySelector('.js-navigation-toggle');
    this.mobileNavigationToggle = this.el.querySelector('.js-mobile-navigation-toggle');

    this.body = document.getElementsByTagName('body')[0];
    this.navigation = document.querySelector('.js-navigation');

    this.messageToggle = this.el.querySelector('.js-message-dropdown-toggle');
    this.messageDropdown = this.el.querySelector('.js-message-dropdown');

    this.chatToggle = this.el.querySelector('.js-chat-dropdown-toggle');
    this.chatDropdown = this.el.querySelector('.js-chat-dropdown');

    this.collapsed = false;

  },

  addEventListeners: function () {

    this.navigationToggle.addEventListener('click', function () {

      this.toggleNavigationCollapse();

    }.bind(this));

    this.mobileNavigationToggle.addEventListener('click', function () {

      this.toggleMobileNavigation();

    }.bind(this));

    if (this.messageToggle && this.messageDropdown) {

      this.messageToggle.addEventListener('click', function () {

        this.toggleDropdown(this.messageDropdown, 'header-menu-message-dropdown--active');

      }.bind(this));

    }

    if (this.chatToggle && this.chatDropdown) {

      this.chatToggle.addEventListener('click', function () {

        this.toggleDropdown(this.chatDropdown, 'header-menu-chat-dropdown--active');

      }.bind(this));

    }

  },

  checkCollapsed: function () {

    if (localStorage.getItem('collapsed')) {
      this.collapsed = JSON.parse(localStorage.getItem('collapsed'));
    }

    if (this.collapsed) {
      this.body.classList.add('navigation-collapsed');
    }

  },

  toggleNavigationCollapse: function () {

    this.collapsed = !this.collapsed;

    this.body.classList.toggle('navigation-collapsed');
    localStorage.setItem('collapsed', this.collapsed);

  },

  toggleMobileNavigation: function () {

    this.navigation.classList.toggle('navigation--active');

  },

  toggleDropdown: function (element, className) {

    element.classList.toggle(className);

  }

};
<commit_msg>Refactored header.js<commit_after>'use strict';

module.exports = function (element) {

  this.el = element;
  this.init();

};

module.exports.prototype = {

  init: function () {

    console.log('Init header');
    this.setVariables();
    this.addEventListeners();
    this.checkCollapsed();

  },

  setVariables: function () {

    this.navigationToggle = this.el.querySelector('.js-navigation-toggle');
    this.mobileNavigationToggle = this.el.querySelector('.js-mobile-navigation-toggle');

    this.body = document.getElementsByTagName('body')[0];
    this.navigation = document.querySelector('.js-navigation');

    this.messageToggle = this.el.querySelector('.js-message-dropdown-toggle');
    this.messageDropdown = this.el.querySelector('.js-message-dropdown');

    this.chatToggle = this.el.querySelector('.js-chat-dropdown-toggle');
    this.chatDropdown = this.el.querySelector('.js-chat-dropdown');

    this.collapsed = false;

  },

  addEventListeners: function () {

    this.navigationToggle.addEventListener('click', function () {

      this.toggleNavigationCollapse();

    }.bind(this));

    this.mobileNavigationToggle.addEventListener('click', function () {

      this.toggleClass(this.navigation, 'navigation--active');

    }.bind(this));

    if (this.messageToggle && this.messageDropdown) {

      this.messageToggle.addEventListener('click', function () {

        this.toggleClass(this.messageDropdown, 'header-menu-message-dropdown--active');

      }.bind(this));

    }

    if (this.chatToggle && this.chatDropdown) {

      this.chatToggle.addEventListener('click', function () {

        this.toggleClass(this.chatDropdown, 'header-menu-chat-dropdown--active');

      }.bind(this));

    }

  },

  checkCollapsed: function () {

    if (localStorage.getItem('collapsed')) {
      this.collapsed = JSON.parse(localStorage.getItem('collapsed'));
    }

    if (this.collapsed) {
      this.body.classList.add('navigation-collapsed');
    }

  },

  toggleNavigationCollapse: function () {

    this.collapsed = !this.collapsed;

    this.toggleClass(this.body, 'navigation-collapsed');
    localStorage.setItem('collapsed', this.collapsed);

  },

  toggleClass: function (element, className) {

    element.classList.toggle(className);

  }

};
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';

import Immutable from 'immutable';

import connectToTailer from './connectToTailer';

import * as Selectors from '../selectors';

import LogLines, { LOG_LINE_HEIGHT } from './LogLines';

class Log extends Component {
  constructor() {
    super();

    this.isLineLoaded = this.isLineLoaded.bind(this);
    this.loadLine = this.loadLine.bind(this);
    this.tailLog = this.tailLog.bind(this);
    this.pollScroll = this.pollScroll.bind(this);

    this.scrollTop = undefined;
    this.scrollHeight = undefined;
    this.invalidate = false;

    this.fakeLineCount = 0;
    this.scrollDelta = 0;
  }

  componentDidMount() {
    if (!this.props.isLoaded) {
      this.props.initializeFile(this.props.goToOffset);
    }

    this.rafRequestId = window.requestAnimationFrame(this.pollScroll);
  }

  componentWillUpdate(nextProps) {
    if (nextProps.lines !== this.props.lines) {
      this.invalidate = true;
    }

    if (nextProps.lines.size > 1 && this.props.lines.size > 1) {
      const oldLines = this.props.lines;
      const newLines = nextProps.lines;

      const addedToBeginning = newLines.findIndex((l) => { return l.start >= oldLines.get(0).end; }) - 1;
      const removedFromBeginning = oldLines.findIndex((l) => { return l.end >= newLines.get(0).end; });

      if (removedFromBeginning) {
        this.fakeLineCount += removedFromBeginning - 1;
      }

      if (addedToBeginning) {
        if (this.fakeLineCount - addedToBeginning >= 0) {
          this.fakeLineCount -= addedToBeginning;
        } else {
          this.scrollDelta += LOG_LINE_HEIGHT * (addedToBeginning - this.fakeLineCount);
          this.fakeLineCount = 0;
        }
      }
    }
  }

  componentDidUpdate(prevProps) {
    const idMatches = this.props.tailerId === prevProps.tailerId;
    const offsetMatches = this.props.goToOffset === prevProps.goToOffset;

    if (!idMatches || !offsetMatches) {
      this.props.initializeFile(this.props.goToOffset);
    }
  }

  componentWillUnmount() {
    window.cancelAnimationFrame(this.rafRequestId);
  }

  isLineLoaded(index) {
    return (
      index < this.props.lines.size
      && (
        !this.props.lines.get(index).isMissingMarker &&
        !this.props.requests.has(this.props.lines.get(index).start)
      )
    );
  }

  // detect when dom has changed underneath us- either scrollTop or scrollHeight (layout reflow)
  // may have changed.
  pollScroll () {
    const domNode = ReactDOM.findDOMNode(this);

    // let's update the scroll now, in a raf.
    if (this.scrollDelta) {
      domNode.scrollTop += this.scrollDelta;
      this.scrollDelta = 0;
    }

    const { scrollTop, scrollHeight, clientHeight } = domNode;
    if (scrollTop !== this.scrollTop || this.invalidate) {
      this.invalidate = false;
      const scrollLoadThreshold = LOG_LINE_HEIGHT * 300;
      const atTop = (scrollTop - this.fakeLineCount * LOG_LINE_HEIGHT) <= scrollLoadThreshold;
      const atBottom = scrollTop >= (scrollHeight - clientHeight - scrollLoadThreshold);

      const { lines } = this.props;
      if (atTop && atBottom && lines.size === 1) {
        // wait until the first chunk is loaded.
      } else {
        // we are at the top/bottom, load some stuff.
        if (atTop && !this.isLineLoaded(0)) {
          setTimeout(() => this.loadLine(0, true), 0);
        }

        if (atBottom) {
          if (lines.size) {
            setTimeout(() => this.loadLine(lines.size - 1, false), 0);
          }
        }
      }
      // update the scroll position.
      this.scrollTop = domNode.scrollTop;
      this.scrollHeight = domNode.scrollHeight;
    }
    // do another raf.
    this.rafRequestId = window.requestAnimationFrame(this.pollScroll);
  }

  loadLine(index, loadUp) {
    return this.props.loadLine(
      index,
      loadUp,
      this.props.lines,
      this.props.chunks
    );
  }

  tailLog() {
    return this.props.tailLog(this.props.lines);
  }

  render() {
    const { props } = this;

    const hrefFunc = props.hrefFunc
      ? (offset) => props.hrefFunc(props.tailerId, offset)
      : undefined;

    return (

          <LogLines
            isLoaded={props.isLoaded}
            lines={props.lines}
            fakeLineCount={this.fakeLineCount}
            isLineLoaded={this.isLineLoaded}
            hrefFunc={hrefFunc}
          />

    );
  }
}

Log.propTypes = {
  tailerId: PropTypes.string.isRequired,
  goToOffset: PropTypes.number,
  hrefFunc: PropTypes.func,
  // from connectToTailer HOC
  getTailerState: PropTypes.func.isRequired,
  // from tailer implementation
  // actions
  initializeFile: PropTypes.func.isRequired,
  loadLine: PropTypes.func.isRequired,
  tailLog: PropTypes.func.isRequired,
  // from connect
  isLoaded: PropTypes.bool.isRequired,
  fileSize: PropTypes.number,
  lines: PropTypes.instanceOf(Immutable.List),
  chunks: PropTypes.instanceOf(Immutable.List),
  requests: PropTypes.instanceOf(Immutable.Map),
  config: PropTypes.object.isRequired
};

const makeMapStateToProps = () => {
  const getEnhancedLines = Selectors.makeGetEnhancedLines();
  const mapStateToProps = (state, ownProps) => ({
    isLoaded: Selectors.getIsLoaded(state, ownProps),
    fileSize: Selectors.getFileSize(state, ownProps),
    lines: getEnhancedLines(state, ownProps),
    chunks: Selectors.getChunks(state, ownProps),
    requests: Selectors.getRequests(state, ownProps),
    config: Selectors.getConfig(state, ownProps)
  });
  return mapStateToProps;
};

export default connectToTailer(connect(
  makeMapStateToProps
)(Log));
<commit_msg>Fix tailing<commit_after>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';

import classNames from 'classnames';

import Immutable from 'immutable';

import connectToTailer from './connectToTailer';

import * as Selectors from '../selectors';

import LogLines, { LOG_LINE_HEIGHT } from './LogLines';

const SCROLL_LOAD_THRESHOLD = 300;
const TAIL_INTERVAL_MS = 5000;

class Log extends Component {
  constructor() {
    super();

    this.isLineLoaded = this.isLineLoaded.bind(this);
    this.loadLine = this.loadLine.bind(this);
    this.tailLog = this.tailLog.bind(this);
    this.pollScroll = this.pollScroll.bind(this);

    this.scrollTop = undefined;
    this.scrollHeight = undefined;
    this.invalidate = false;

    this.fakeLineCount = 0;
    this.scrollDelta = 0;

    this.state = {
      tailing: false
    };
  }

  componentDidMount() {
    if (!this.props.isLoaded) {
      this.props.initializeFile(this.props.goToOffset);
    }

    this.rafRequestId = window.requestAnimationFrame(this.pollScroll);
  }

  componentWillUpdate(nextProps, nextState) {
    if (nextProps.lines !== this.props.lines) {
      this.invalidate = true;
      if (this.state.tailing) {
        this.scrollDelta = -1;
      }
    }

    if (nextProps.lines.size > 1 && this.props.lines.size > 1) {
      const oldLines = this.props.lines;
      const newLines = nextProps.lines;

      const addedToBeginning = newLines.findIndex((l) => { return l.start >= oldLines.get(0).end; }) - 1;
      const removedFromBeginning = oldLines.findIndex((l) => { return l.end >= newLines.get(0).end; });

      if (removedFromBeginning) {
        this.fakeLineCount += removedFromBeginning - 1;
      }

      if (addedToBeginning) {
        if (this.fakeLineCount - addedToBeginning >= 0) {
          this.fakeLineCount -= addedToBeginning;
        } else {
          this.scrollDelta += LOG_LINE_HEIGHT * (addedToBeginning - this.fakeLineCount);
          this.fakeLineCount = 0;
        }
      }
    }

    if (nextState.tailing && this.tailIntervalId == null) {
      this.tailIntervalId = setInterval(() => {
        this.loadLine(this.props.lines.size - 1, false);
      }, TAIL_INTERVAL_MS);
    } else if (!nextState.tailing && this.tailIntervalId != null) {
      clearInterval(this.tailIntervalId);
    }
  }

  componentDidUpdate(prevProps) {
    const idMatches = this.props.tailerId === prevProps.tailerId;
    const offsetMatches = this.props.goToOffset === prevProps.goToOffset;

    if (!idMatches || !offsetMatches) {
      this.props.initializeFile(this.props.goToOffset);
    }
  }

  componentWillUnmount() {
    window.cancelAnimationFrame(this.rafRequestId);
    clearInterval(this.tailIntervalId);
  }

  isLineLoaded(index) {
    return (
      index < this.props.lines.size
      && (
        !this.props.lines.get(index).isMissingMarker &&
        !this.props.requests.has(this.props.lines.get(index).start)
      )
    );
  }

  // detect when dom has changed underneath us- either scrollTop or scrollHeight (layout reflow)
  // may have changed.
  pollScroll () {
    const domNode = ReactDOM.findDOMNode(this);

    // let's update the scroll now, in a raf.
    if (this.scrollDelta > 0) {
      domNode.scrollTop += this.scrollDelta;
      this.scrollDelta = 0;
    } else if (this.scrollDelta === -1) {
      domNode.scrollTop = domNode.scrollHeight;
      this.scrollDelta = 0;
    }

    const { scrollTop, scrollHeight, clientHeight } = domNode;
    if (scrollTop !== this.scrollTop || this.invalidate) {
      this.invalidate = false;
      const scrollLoadThreshold = LOG_LINE_HEIGHT * SCROLL_LOAD_THRESHOLD;
      const nearTop = (scrollTop - this.fakeLineCount * LOG_LINE_HEIGHT) <= scrollLoadThreshold;
      const nearBottom = scrollTop >= (scrollHeight - clientHeight - scrollLoadThreshold);
      const atBottom = scrollTop === (scrollHeight - clientHeight);

      const { lines } = this.props;
      if (nearTop && nearBottom && lines.size === 1) {
        // wait until the first chunk is loaded.
      } else {
        // we are at the top/bottom, load some stuff.
        if (nearTop && !this.isLineLoaded(0)) {
          // don't dispatch in the raf, do it later
          setTimeout(() => this.loadLine(0, true), 0);
        }

        if (nearBottom && lines.size) {
          // if we haven't reached the end of the file yet
          if (lines.last().isMissingMarker) {
            // don't dispatch in the raf, do it later
            setTimeout(() => this.loadLine(lines.size - 1, false), 0);
          } else if (atBottom) {
            // we're tailing here.
            if (!this.state.tailing) {
              this.setState({
                tailing: true
              });
            }
          }
        }
      }

      if (!atBottom && this.state.tailing) {
        this.setState({
          tailing: false
        });
      }
      // update the scroll position.
      this.scrollTop = domNode.scrollTop;
      this.scrollHeight = domNode.scrollHeight;
    }
    // do another raf.
    this.rafRequestId = window.requestAnimationFrame(this.pollScroll);
  }

  loadLine(index, loadUp) {
    return this.props.loadLine(
      index,
      loadUp,
      this.props.lines,
      this.props.chunks
    );
  }

  tailLog() {
    return this.props.tailLog(this.props.lines);
  }

  render() {
    const { props } = this;

    const hrefFunc = props.hrefFunc
      ? (offset) => props.hrefFunc(props.tailerId, offset)
      : undefined;

    const logPaneClasses = classNames({
      'log-pane': true,
      tailing: this.state.tailing
    });

    return (

          <LogLines
            isLoaded={props.isLoaded}
            lines={props.lines}
            fakeLineCount={this.fakeLineCount}
            isLineLoaded={this.isLineLoaded}
            hrefFunc={hrefFunc}
          />

    );
  }
}

Log.propTypes = {
  tailerId: PropTypes.string.isRequired,
  goToOffset: PropTypes.number,
  hrefFunc: PropTypes.func,
  // from connectToTailer HOC
  getTailerState: PropTypes.func.isRequired,
  // from tailer implementation
  // actions
  initializeFile: PropTypes.func.isRequired,
  loadLine: PropTypes.func.isRequired,
  tailLog: PropTypes.func.isRequired,
  // from connect
  isLoaded: PropTypes.bool.isRequired,
  fileSize: PropTypes.number,
  lines: PropTypes.instanceOf(Immutable.List),
  chunks: PropTypes.instanceOf(Immutable.List),
  requests: PropTypes.instanceOf(Immutable.Map),
  config: PropTypes.object.isRequired
};

const makeMapStateToProps = () => {
  const getEnhancedLines = Selectors.makeGetEnhancedLines();
  const mapStateToProps = (state, ownProps) => ({
    isLoaded: Selectors.getIsLoaded(state, ownProps),
    fileSize: Selectors.getFileSize(state, ownProps),
    lines: getEnhancedLines(state, ownProps),
    chunks: Selectors.getChunks(state, ownProps),
    requests: Selectors.getRequests(state, ownProps),
    config: Selectors.getConfig(state, ownProps)
  });
  return mapStateToProps;
};

export default connectToTailer(connect(
  makeMapStateToProps
)(Log));
<|endoftext|>