<commit_before>/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; 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 angular = require('../../../../../../../camunda-commons-ui/vendor/angular');

module.exports = [
  '$scope',
  '$q',
  'Uri',
  'camAPI',
  'Views',
  '$translate',
  function($scope, $q, Uri, camAPI, Views, $translate) {
    // utilities ///////////////////////////////////////////////////

    var isObject = angular.isObject;

    var checkResource = function(name, pattern) {
      return name && pattern.test(name.toLowerCase());
    };

    // fields //////////////////////////////////////////////////////

    var resourceDetailsData = ($scope.resourceDetailsData = $scope.repositoryData.newChild(
      $scope
    ));

    var Deployment = camAPI.resource('deployment');

    var control = ($scope.control = {});

    var BPMN_PATTERN = /\.(bpmn\d*.xml|bpmn)$/;
    var CMMN_PATTERN = /\.(cmmn\d*.xml|cmmn)$/;
    var DMN_PATTERN = /\.(dmn\d*.xml|dmn)$/;
    var IMAGE_PATTERN = /\.(gif|jpg|jpeg|jpe|png|svg|tif|tiff)$/;

    var PLUGIN_ACTION_COMPONENT = 'cockpit.repository.resource.action';

    // type of a resource //////////////////////////////////////////

    var getResourceName = function(resource) {
      if (isObject(resource)) {
        return resource.name;
      }
      return resource;
    };

    var isBpmnResource = (control.isBpmnResource = $scope.isBpmnResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, BPMN_PATTERN);
    });

    var isCmmnResource = (control.isCmmnResource = $scope.isCmmnResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, CMMN_PATTERN);
    });

    var isDmnResource = (control.isDmnResource = $scope.isDmnResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, DMN_PATTERN);
    });

    var isImageResource = (control.isImageResource = $scope.isImageResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, IMAGE_PATTERN);
    });

    control.isUnkownResource = $scope.isUnkownResource = function(resource) {
      return (
        !isBpmnResource(resource) &&
        !isCmmnResource(resource) &&
        !isDmnResource(resource) &&
        !isImageResource(resource)
      );
    };

    var ProcessDefinition = camAPI.resource('process-definition');
    var CaseDefinition = camAPI.resource('case-definition');
    var DecisionDefinition = camAPI.resource('decision-definition');

    // download link ////////////////////////////////////////////////

    control.downloadLink = $scope.downloadLink = function(
      deployment,
      resource
    ) {
      return (
        deployment &&
        resource &&
        Uri.appUri(
          'engine://engine/:engine/deployment/' +
            deployment.id +
            '/resources/' +
            resource.id +
            '/data'
        )
      );
    };

    // provide //////////////////////////////////////////////////////

    resourceDetailsData.provide('binary', [
      'resource',
      'currentDeployment',
      function(resource, deployment) {
        var deferred = $q.defer();

        if (!resource) {
          deferred.resolve(null);
        } else if (!deployment || deployment.id === null) {
          deferred.resolve(null);
        } else if (isImageResource(resource)) {
          // do not load image twice
          deferred.resolve(null);
        } else {
          Deployment.getResourceData(deployment.id, resource.id, function(
            err,
            res
          ) {
            if (err) {
              deferred.reject(err);
            } else {
              deferred.resolve({data: res});
            }
          });
        }

        return deferred.promise;
      }
    ]);

    var pages = {current: 1, size: 50, total: 0};
    resourceDetailsData.provide('pages', function() {
      return pages;
    });

    resourceDetailsData.provide('definitions', [
      'currentDeployment',
      'resource',
      'pages',
      function(deployment, resource, pages) {
        var deferred = $q.defer();

        $scope.loadingState = 'LOADING';

        var Service = null;
        var bpmnResource = false;

        if (!deployment || !resource) {
          deferred.resolve([]);
        } else {
          if (isBpmnResource(resource)) {
            bpmnResource = true;
            Service = ProcessDefinition;
          } else if (isCmmnResource(resource)) {
            Service = CaseDefinition;
          } else if (isDmnResource(resource)) {
            Service = DecisionDefinition;
          }

          if (!Service) {
            deferred.resolve([]);
          } else {
            Service.count(
              {
                deploymentId: deployment.id,
                resourceName: resource.name
              },
              function(err, res) {
                if (err) {
                  $scope.loadingState = 'ERROR';
                  $scope.textError =
                    err.message ||
                    $translate.instant(
                      'REPOSITORY_DEPLOYMENT_RESOURCE_CTRL_MSN'
                    );
                  return deferred.reject(err);
                }

                pages.total = res;

                Service.list(
                  {
                    deploymentId: deployment.id,
                    resourceName: resource.name,
                    maxResults: pages.size,
                    firstResult: pages.size * (pages.current - 1)
                  },
                  function(err, res) {
                    if (err) {
                      $scope.loadingState = 'ERROR';
                      $scope.textError =
                        err.message ||
                        $translate.instant(
                          'REPOSITORY_DEPLOYMENT_RESOURCE_CTRL_MSN'
                        );
                      return deferred.reject(err);
                    }

                    deferred.resolve(bpmnResource ? res.items : res);
                  }
                );
              }
            );
          }
        }

        return deferred.promise;
      }
    ]);

    // observe /////////////////////////////////////////////////

    resourceDetailsData.observe('resource', function(resource) {
      $scope.resource = resource;
    });

    resourceDetailsData.observe('currentDeployment', function(deployment) {
      $scope.deployment = deployment;
    });

    // plugins //////////////////////////////////////////////////

    $scope.resourceVars = {
      read: ['control', 'deployment', 'resource', 'resourceDetailsData']
    };
    $scope.resourceActions = Views.getProviders({
      component: PLUGIN_ACTION_COMPONENT
    });
  }
];
<commit_msg>chore(deployment): refactor resource details fetching<commit_after>/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; 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 angular = require('../../../../../../../camunda-commons-ui/vendor/angular');

module.exports = [
  '$scope',
  '$q',
  'Uri',
  'camAPI',
  'Views',
  '$translate',
  function($scope, $q, Uri, camAPI, Views, $translate) {
    // utilities ///////////////////////////////////////////////////

    var isObject = angular.isObject;

    var checkResource = function(name, pattern) {
      return name && pattern.test(name.toLowerCase());
    };

    // fields //////////////////////////////////////////////////////

    var resourceDetailsData = ($scope.resourceDetailsData = $scope.repositoryData.newChild(
      $scope
    ));

    var Deployment = camAPI.resource('deployment');

    var control = ($scope.control = {});

    var BPMN_PATTERN = /\.(bpmn\d*.xml|bpmn)$/;
    var CMMN_PATTERN = /\.(cmmn\d*.xml|cmmn)$/;
    var DMN_PATTERN = /\.(dmn\d*.xml|dmn)$/;
    var IMAGE_PATTERN = /\.(gif|jpg|jpeg|jpe|png|svg|tif|tiff)$/;

    var PLUGIN_ACTION_COMPONENT = 'cockpit.repository.resource.action';

    // type of a resource //////////////////////////////////////////

    var getResourceName = function(resource) {
      if (isObject(resource)) {
        return resource.name;
      }
      return resource;
    };

    var isBpmnResource = (control.isBpmnResource = $scope.isBpmnResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, BPMN_PATTERN);
    });

    var isCmmnResource = (control.isCmmnResource = $scope.isCmmnResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, CMMN_PATTERN);
    });

    var isDmnResource = (control.isDmnResource = $scope.isDmnResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, DMN_PATTERN);
    });

    var isImageResource = (control.isImageResource = $scope.isImageResource = function(
      resource
    ) {
      var resourceName = getResourceName(resource);
      return checkResource(resourceName, IMAGE_PATTERN);
    });

    control.isUnkownResource = $scope.isUnkownResource = function(resource) {
      return (
        !isBpmnResource(resource) &&
        !isCmmnResource(resource) &&
        !isDmnResource(resource) &&
        !isImageResource(resource)
      );
    };

    var ProcessDefinition = camAPI.resource('process-definition');
    var CaseDefinition = camAPI.resource('case-definition');
    var DecisionDefinition = camAPI.resource('decision-definition');

    // download link ////////////////////////////////////////////////

    control.downloadLink = $scope.downloadLink = function(
      deployment,
      resource
    ) {
      return (
        deployment &&
        resource &&
        Uri.appUri(
          'engine://engine/:engine/deployment/' +
            deployment.id +
            '/resources/' +
            resource.id +
            '/data'
        )
      );
    };

    // provide //////////////////////////////////////////////////////

    resourceDetailsData.provide('binary', [
      'resource',
      'currentDeployment',
      function(resource, deployment) {
        var deferred = $q.defer();

        if (!resource) {
          deferred.resolve(null);
        } else if (!deployment || deployment.id === null) {
          deferred.resolve(null);
        } else if (isImageResource(resource)) {
          // do not load image twice
          deferred.resolve(null);
        } else {
          Deployment.getResourceData(deployment.id, resource.id, function(
            err,
            res
          ) {
            if (err) {
              deferred.reject(err);
            } else {
              deferred.resolve({data: res});
            }
          });
        }

        return deferred.promise;
      }
    ]);

    var pages = {current: 1, size: 50, total: 0};
    resourceDetailsData.provide('pages', function() {
      return pages;
    });

    resourceDetailsData.provide('definitions', [
      'currentDeployment',
      'resource',
      'pages',
      function(deployment, resource, pages) {
        var deferred = $q.defer();

        $scope.loadingState = 'LOADING';

        var Service = null;
        var bpmnResource = false;

        if (!deployment || !resource) {
          deferred.resolve([]);
        } else {
          if (isBpmnResource(resource)) {
            bpmnResource = true;
            Service = ProcessDefinition;
          } else if (isCmmnResource(resource)) {
            Service = CaseDefinition;
          } else if (isDmnResource(resource)) {
            Service = DecisionDefinition;
          }

          if (!Service) {
            deferred.resolve([]);
          } else {
            Service.count(
              {
                deploymentId: deployment.id,
                resourceName: resource.name
              },
              function(err, res) {
                function handleError(err) {
                  $scope.loadingState = 'ERROR';
                  $scope.textError =
                    err.message ||
                    $translate.instant(
                      'REPOSITORY_DEPLOYMENT_RESOURCE_CTRL_MSN'
                    );
                }

                if (err) {
                  handleError(err);
                  return deferred.reject(err);
                }

                pages.total = res;

                if (res === 0) {
                  // There are no definitions in this resource
                  return deferred.resolve([]);
                }

                Service.list(
                  {
                    deploymentId: deployment.id,
                    resourceName: resource.name,
                    maxResults: pages.size,
                    firstResult: pages.size * (pages.current - 1)
                  },
                  function(err, res) {
                    if (err) {
                      handleError(err);
                      return deferred.reject(err);
                    }

                    deferred.resolve(bpmnResource ? res.items : res);
                  }
                );
              }
            );
          }
        }

        return deferred.promise;
      }
    ]);

    // observe /////////////////////////////////////////////////

    resourceDetailsData.observe('resource', function(resource) {
      $scope.resource = resource;
    });

    resourceDetailsData.observe('currentDeployment', function(deployment) {
      $scope.deployment = deployment;
    });

    // plugins //////////////////////////////////////////////////

    $scope.resourceVars = {
      read: ['control', 'deployment', 'resource', 'resourceDetailsData']
    };
    $scope.resourceActions = Views.getProviders({
      component: PLUGIN_ACTION_COMPONENT
    });
  }
];
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. 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.
 */

CLASS({
  package: 'com.google.watlobby',
  name: 'Lobby',
  extends: 'foam.graphics.CView',
  traits: [ 'com.google.misc.Colors' ],

  requires: [
    'com.google.watlobby.SmallRemote',
    'com.google.watlobby.Remote',
    'com.google.watlobby.TopicApp',
    'foam.dao.EasyDAO',
    'com.google.watlobby.Bubble',
    'com.google.watlobby.TopicBubble',
    'com.google.watlobby.AlbumBubble',
    'com.google.watlobby.Topic',
    'com.google.watlobby.VideoBubble',
    'foam.demos.ClockView',
    'foam.demos.physics.PhysicalCircle',
    'foam.graphics.ImageCView',
    'foam.physics.PhysicsEngine as Collider',
    'foam.util.Timer'
  ],

  imports: [ 'timer', 'clearTimeout', 'setTimeout' ],
  exports: [ 'as lobby' ],

  properties: [
    { name: 'timer' },
    { name: 'clientMode', defaultValue: false, model_: 'BooleanProperty', postSet: function() { console.log("client mode", this.clientMode); } },
    { name: 'n',          defaultValue: 25 },
    { name: 'slideshowDelay', model_: 'IntProperty' },
    { name: 'airBubbles', defaultValue: 0, model_: 'IntProperty' },
    { name: 'width',      defaultValue: window.innerWidth },
    { name: 'height',     defaultValue: window.innerHeight },
    { name: 'background', defaultValue: '#ccf' },
    { name: 'collider',   factory: function() {
      var c = this.Collider.create();
      var w = this.width;
      var h = this.height;
      var self = this;
      c.detectCollisions = function() {
        var cs = this.children;
        for ( var i = 0 ; i < cs.length ; i++ ) {
          var c1 = cs[i];
          this.updateChild(c1);
          var r = c1.r + 10;

          // Add Coriolis Effect
          var a = Math.atan2(c1.y-h/2, c1.x-w/2);
          var d = Movement.distance(c1.y-h/2, c1.x-w/2);
          // Keeps topic bubbles from going too far off screen
          // if ( c1.topic && d > h / 2 - r) c1.out_ = false;
          if ( d > w / 2 - r ) c1.out_ = false;
          if ( d < h/4 ) c1.out_ = true;
          // c1.color = c1.out_ ? 'orange' : 'blue';

          // The 0.9 gives it a slight outward push
          if ( c1.mass != c1.INFINITE_MASS )
            c1.applyMomentum((0.5+0.4*c1.$UID%11/10) * c1.mass/4, a+(c1.out_ ? 0.9 : 1.1)*Math.PI/2);

          // Make collision detection 5X faster by only checking every fifth time.
          if ( ( self.timer.i + i ) % 5 == 0 ) for ( var j = i+1 ; j < cs.length ; j++ ) {
            var c2 = cs[j];
            if ( c1.intersects(c2) ) this.collide(c1, c2);
          }
        }
      };
      return c;
    }},
    {
      name: 'topics',
      lazyFactory: function() {
	var dao = this.EasyDAO.create({
          daoType: 'MDAO',
          model: this.Topic,
          cloning: true,
          contextualize: true,
          guid: true,
          syncWithServer: this.clientMode
        });
        return dao;

        if ( this.clientMode ) return dao;

        axhr('topics.json')(function(topics) {
          JSONUtil.arrayToObjArray(this.X, topics, this.Topic).select(dao);
        }.bind(this));

        return dao;
      }
    },
    {
      name: 'selected',
      preSet: function(o, n) {
        if ( o === n ) return o;

        if ( o ) o.setSelected(false);

        if ( n && n.setSelected ) {
          // Move-to-Front
          var i = this.children.indexOf(n);
          this.children[i] = this.children[this.children.length-1];
          this.children[this.children.length-1] = n;

          n.setSelected(true);

          this.clearTimeout(this.timeout_);
          this.timeout_ = this.setTimeout(function() { this.selected = null; }.bind(this), n.topic.timeout * 1000);

          return n;
        }

        return null;
      }
    }
  ],

  listeners: [
    {
      name: 'onClick',
      code: function(evt) {
        this.selected = this.findChildAt(evt.clientX, evt.clientY);
      }
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      GLOBAL.lobby = this;

      if ( ! this.timer ) {
        this.timer = this.Timer.create();
        this.timer.start();
      }

      this.addBubbles();

      this.topics.pipe({
        put:    this.putTopic.bind(this),
        remove: this.removeTopic.bind(this)
      });

      document.body.addEventListener('click', this.onClick);

      var foam = this.ImageCView.create({x: 10, y: this.height-60, width: 837/5, height: 269/5, src: 'img/foampowered_red.png'});
      this.addChild(foam);

      var clock = this.ClockView.create({
        drawTicks: true,
        x: this.width-250,
        y: 250,
        r: (120-10)/2,
        scaleX: 4,
        scaleY: 4});
      this.addChild(clock);

      this.collider.start();

      if ( this.slideshowDelay ) {
        var i = 0;
        var l = function() {
          var t = this.topics[i++];
          this.selected = this.children[this.findTopic(t)];
          if ( i == this.topics.length ) i = 0;
          this.X.setTimeout(l, t.hasOwnProperty('timeout') ? t.timeout*1000 : this.slideshowDelay*1000);
        }.bind(this);

        l();
      }
    },
    function findTopic(t) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var c = this.children[i];
        if ( c.topic && c.topic.topic === t.topic ) return i;
      }
      return -1;
    },
    function putTopic(t) {
      if ( t.model === 'Background' ) {
        document.body.style.backgroundImage = 'url(' + t.image + ')';
        return;
      }
//      console.log('***** putTopic: ', t.topic);
      var i = this.findTopic(t);
      if ( i != -1 ) {
        if ( t.selected ) {
          this.selected = this.children[i];
          return;
        } else {
          this.children.splice(i, 1);
        }
      }

      if ( ! t.enabled ) return;

      var c = this.X.lookup('com.google.watlobby.' + t.model + 'Bubble').create({
        x: Math.random() * this.width,
        y: Math.random() * this.height,
        border: t.color
      }, this.Y);
      c.topic = t;
      c.image = t.image;
      var r = t.r;
      t.r = 1;
      Movement.animate(2000, function() { t.r = r; })();
      c.roundImage = t.roundImage;
     // if ( t.color ) c.border = t.color;
      if ( t.background ) c.color = t.background;
      this.addChild(c);
      c.mass = r/150;
      c.gravity = 0;
      c.friction = 0.94;
      this.collider.add(c);
    },
    function removeTopic(t) {
      console.log('removeTopic ************** ', arguments);
      // TODO
    },
    function addBubbles() {
      var N = this.n;
      for ( var i = 0 ; i < N ; i++ ) {
        var color = this.COLORS[Math.floor(i / N * this.COLORS.length)];
        var c = this.Bubble.create({
          r: 10 + Math.random() * 60,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          color: null,
          border: color
        });
        this.addChild(c);

        c.mass = c.r/150;
        c.gravity = 0;
        c.friction = 0.94;
        this.collider.add(c);
      }
    },
    function openSmallRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.SmallRemote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.Remote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openAdminUI() {
      var w = foam.ui.Window.create({window: window.open("", "Admin", "width=1200, height=700, location=no, menubar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Admin</title><base href="/js/com/google/watlobby/">');
      var r = this.TopicApp.create({dao: this.topics}, w.Y);
      r.write(w.Y);
    },
    function destroy() {
      this.SUPER();
      this.collider.destroy();
    }
  ]
});
<commit_msg>WatLobby: Cleanup debugging code in Lobby, fix standalong mode.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. 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.
 */

CLASS({
  package: 'com.google.watlobby',
  name: 'Lobby',
  extends: 'foam.graphics.CView',
  traits: [ 'com.google.misc.Colors' ],

  requires: [
    'com.google.watlobby.SmallRemote',
    'com.google.watlobby.Remote',
    'com.google.watlobby.TopicApp',
    'foam.dao.EasyDAO',
    'com.google.watlobby.Bubble',
    'com.google.watlobby.TopicBubble',
    'com.google.watlobby.AlbumBubble',
    'com.google.watlobby.Topic',
    'com.google.watlobby.VideoBubble',
    'foam.demos.ClockView',
    'foam.demos.physics.PhysicalCircle',
    'foam.graphics.ImageCView',
    'foam.physics.PhysicsEngine as Collider',
    'foam.util.Timer'
  ],

  imports: [ 'timer', 'clearTimeout', 'setTimeout' ],
  exports: [ 'as lobby' ],

  properties: [
    { name: 'timer' },
    { name: 'clientMode', defaultValue: false, model_: 'BooleanProperty' },
    { name: 'n',          defaultValue: 25 },
    { name: 'slideshowDelay', model_: 'IntProperty' },
    { name: 'airBubbles', defaultValue: 0, model_: 'IntProperty' },
    { name: 'width',      defaultValue: window.innerWidth },
    { name: 'height',     defaultValue: window.innerHeight },
    { name: 'background', defaultValue: '#ccf' },
    { name: 'collider',   factory: function() {
      var c = this.Collider.create();
      var w = this.width;
      var h = this.height;
      var self = this;
      c.detectCollisions = function() {
        var cs = this.children;
        for ( var i = 0 ; i < cs.length ; i++ ) {
          var c1 = cs[i];
          this.updateChild(c1);
          var r = c1.r + 10;

          // Add Coriolis Effect
          var a = Math.atan2(c1.y-h/2, c1.x-w/2);
          var d = Movement.distance(c1.y-h/2, c1.x-w/2);
          // Keeps topic bubbles from going too far off screen
          // if ( c1.topic && d > h / 2 - r) c1.out_ = false;
          if ( d > w / 2 - r ) c1.out_ = false;
          if ( d < h/4 ) c1.out_ = true;
          // c1.color = c1.out_ ? 'orange' : 'blue';

          // The 0.9 gives it a slight outward push
          if ( c1.mass != c1.INFINITE_MASS )
            c1.applyMomentum((0.5+0.4*c1.$UID%11/10) * c1.mass/4, a+(c1.out_ ? 0.9 : 1.1)*Math.PI/2);

          // Make collision detection 5X faster by only checking every fifth time.
          if ( ( self.timer.i + i ) % 5 == 0 ) for ( var j = i+1 ; j < cs.length ; j++ ) {
            var c2 = cs[j];
            if ( c1.intersects(c2) ) this.collide(c1, c2);
          }
        }
      };
      return c;
    }},
    {
      name: 'topics',
      lazyFactory: function() {
	var dao = this.EasyDAO.create({
          daoType: 'MDAO',
          model: this.Topic,
          cloning: true,
          contextualize: true,
          guid: true,
          syncWithServer: this.clientMode
        });

        if ( this.clientMode ) return dao;

        axhr('topics.json')(function(topics) {
          JSONUtil.arrayToObjArray(this.X, topics, this.Topic).select(dao);
        }.bind(this));

        return dao;
      }
    },
    {
      name: 'selected',
      preSet: function(o, n) {
        if ( o === n ) return o;

        if ( o ) o.setSelected(false);

        if ( n && n.setSelected ) {
          // Move-to-Front
          var i = this.children.indexOf(n);
          this.children[i] = this.children[this.children.length-1];
          this.children[this.children.length-1] = n;

          n.setSelected(true);

          this.clearTimeout(this.timeout_);
          this.timeout_ = this.setTimeout(function() { this.selected = null; }.bind(this), n.topic.timeout * 1000);

          return n;
        }

        return null;
      }
    }
  ],

  listeners: [
    {
      name: 'onClick',
      code: function(evt) {
        this.selected = this.findChildAt(evt.clientX, evt.clientY);
      }
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      GLOBAL.lobby = this;

      if ( ! this.timer ) {
        this.timer = this.Timer.create();
        this.timer.start();
      }

      this.addBubbles();

      this.topics.pipe({
        put:    this.putTopic.bind(this),
        remove: this.removeTopic.bind(this)
      });

      document.body.addEventListener('click', this.onClick);

      var foam = this.ImageCView.create({x: 10, y: this.height-60, width: 837/5, height: 269/5, src: 'img/foampowered_red.png'});
      this.addChild(foam);

      var clock = this.ClockView.create({
        drawTicks: true,
        x: this.width-250,
        y: 250,
        r: (120-10)/2,
        scaleX: 4,
        scaleY: 4});
      this.addChild(clock);

      this.collider.start();

      if ( this.slideshowDelay ) {
        var i = 0;
        var l = function() {
          var t = this.topics[i++];
          this.selected = this.children[this.findTopic(t)];
          if ( i == this.topics.length ) i = 0;
          this.X.setTimeout(l, t.hasOwnProperty('timeout') ? t.timeout*1000 : this.slideshowDelay*1000);
        }.bind(this);

        l();
      }
    },
    function findTopic(t) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var c = this.children[i];
        if ( c.topic && c.topic.topic === t.topic ) return i;
      }
      return -1;
    },
    function putTopic(t) {
      if ( t.model === 'Background' ) {
        document.body.style.backgroundImage = 'url(' + t.image + ')';
        return;
      }
//      console.log('***** putTopic: ', t.topic);
      var i = this.findTopic(t);
      if ( i != -1 ) {
        if ( t.selected ) {
          this.selected = this.children[i];
          return;
        } else {
          this.children.splice(i, 1);
        }
      }

      if ( ! t.enabled ) return;

      var c = this.X.lookup('com.google.watlobby.' + t.model + 'Bubble').create({
        x: Math.random() * this.width,
        y: Math.random() * this.height,
        border: t.color
      }, this.Y);
      c.topic = t;
      c.image = t.image;
      var r = t.r;
      t.r = 1;
      Movement.animate(2000, function() { t.r = r; })();
      c.roundImage = t.roundImage;
     // if ( t.color ) c.border = t.color;
      if ( t.background ) c.color = t.background;
      this.addChild(c);
      c.mass = r/150;
      c.gravity = 0;
      c.friction = 0.94;
      this.collider.add(c);
    },
    function removeTopic(t) {
      console.log('removeTopic ************** ', arguments);
      // TODO
    },
    function addBubbles() {
      var N = this.n;
      for ( var i = 0 ; i < N ; i++ ) {
        var color = this.COLORS[Math.floor(i / N * this.COLORS.length)];
        var c = this.Bubble.create({
          r: 10 + Math.random() * 60,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          color: null,
          border: color
        });
        this.addChild(c);

        c.mass = c.r/150;
        c.gravity = 0;
        c.friction = 0.94;
        this.collider.add(c);
      }
    },
    function openSmallRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.SmallRemote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.Remote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openAdminUI() {
      var w = foam.ui.Window.create({window: window.open("", "Admin", "width=1200, height=700, location=no, menubar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Admin</title><base href="/js/com/google/watlobby/">');
      var r = this.TopicApp.create({dao: this.topics}, w.Y);
      r.write(w.Y);
    },
    function destroy() {
      this.SUPER();
      this.collider.destroy();
    }
  ]
});
<|endoftext|>


<commit_before>require(['jquery'], function ($)
{
    $(document).ready(function ()
    {
        var _mandatoryFields = $(".mandatory :input:not([type='hidden'])");
        var _checkedFields = $(".checked :input:not([type='hidden'])");
        var externalId = $("#PhenoTips\\.PatientClass_0_external_id");
        var _checkedDates = $(".checked-date input.fuzzy-date");
        var _mandatoryDates = $(".mandatory-date input.fuzzy-date");
        var mandatoryFields = _mandatoryFields.add(_checkedFields).add(externalId).add(_checkedDates).add(_mandatoryDates);
        var saveButtons = $("input[name='action_save']");

        //This will happen if the page is not in edit mode
        if ($("#inline").length < 1) {
            return;
        }

        var fieldsByPosition = [];
        mandatoryFields.each(function (index, field)
        {
            var jField = $(field);
            var position = jField.offset().top;
            var parent = $(jField.parents('.chapter')[0]);
            //Position could be 0, so in that case take the position of the parent section
            if (position == 0) {
                position = parent.offset().top;
            }
            var positionObject = {'field': jField, 'position': position, 'parent': parent};
            fieldsByPosition.push(positionObject);
        });

        /* Listeners */
        //Scroll
        var _window = $(window);
        var viewportHeight = $(window).height();
        _window.scroll(function ()
        {
            var windowScroll = _window.scrollTop();
            var lowerVisibilityBound = windowScroll + (viewportHeight / 3);
            var sectionToExpand = null;
            $.each(fieldsByPosition, function (index, fieldObject)
            {
                //Unfortunately, it seems that the position for all objects has to be recalculated on each scroll event
                var position = fieldObject['field'].offset().top;
                if (fieldObject['parent'].hasClass('collapsed')) {
                    position = fieldObject['parent'].offset().top;
                }
                if (position > windowScroll && position < lowerVisibilityBound) {
                    /* Only one section can be expanded at a time. Expands only if the fields inside fail validation. */
                    if (fieldObject['field'][0].__validation && fieldObject['field'][0].__validation.validate() == false) {
                        sectionToExpand == null ? sectionToExpand = fieldObject['parent'] : null;
                    }
                }
            });
            if (sectionToExpand) {
                sectionToExpand.removeClass('collapsed');
            }
        });

        //In case somebody decides to resize the window, and throw off visibility calculations
        _window.resize(function ()
        {
            viewportHeight = $(window).height();
        });

        saveButtons.on("click", function(){
            var numberMissing = 0;
            $.each(fieldsByPosition, function (index, fieldObject)
            {
                if (fieldObject['field'][0].__validation && !fieldObject['field'][0].__validation.validate()) {
                    fieldObject['parent'].removeClass('collapsed');
                    var position = fieldObject['field'].offset().top - (viewportHeight / 3);
                    position < 0 ? position = 0 : null;
                    _window.scrollTop(position);
                    numberMissing++;
                }
            });
            if (numberMissing > 0) {
                var dialog = new PhenoTips.widgets.ModalPopup("There are " + numberMissing + " missing fields",
                    false, {'title': 'Missing fields', 'verticalPosition': 'top', 'removeOnClose': true});
                dialog.showDialog();
                document.fire("phenotips:scrollValidation:invalid");
            }
        });

        var addNewElement = function(field) {
            var jField = $(field);
            var position = jField.offset().top;
            var parent = $(jField.parents('.chapter')[0]);
            //Position could be 0, so in that case take the position of the parent section
            if (position == 0) {
                position = parent.offset().top;
            }
            var positionObject = {'field': jField, 'position': position, 'parent': parent};
            fieldsByPosition.push(positionObject);
        };

        document.observe('xwiki:dom:updated', function (event) {
            ((event && event.memo.elements) || [$('body')]).each(function(element) {
                element.select(".mandatory :input:not([type='hidden'])").each(function(field) {
                    addNewElement(field);
                });
                element.select(".checked :input:not([type='hidden'])").each(function(field) {
                    addNewElement(field);
                });
                // Also validate any date elements.
                element.select(".mandatory-date input.fuzzy-date").each(function(field) {
                  addNewElement(field);
                });
                element.select(".checked-date input.fuzzy-date").each(function(field) {
                  addNewElement(field);
                });
            });
        });
    });
});
<commit_msg>[misc] Slightly improved message for invalid inputs<commit_after>require(['jquery'], function ($)
{
    $(document).ready(function ()
    {
        var _mandatoryFields = $(".mandatory :input:not([type='hidden'])");
        var _checkedFields = $(".checked :input:not([type='hidden'])");
        var externalId = $("#PhenoTips\\.PatientClass_0_external_id");
        var _checkedDates = $(".checked-date input.fuzzy-date");
        var _mandatoryDates = $(".mandatory-date input.fuzzy-date");
        var mandatoryFields = _mandatoryFields.add(_checkedFields).add(externalId).add(_checkedDates).add(_mandatoryDates);
        var saveButtons = $("input[name='action_save']");

        //This will happen if the page is not in edit mode
        if ($("#inline").length < 1) {
            return;
        }

        var fieldsByPosition = [];
        mandatoryFields.each(function (index, field)
        {
            var jField = $(field);
            var position = jField.offset().top;
            var parent = $(jField.parents('.chapter')[0]);
            //Position could be 0, so in that case take the position of the parent section
            if (position == 0) {
                position = parent.offset().top;
            }
            var positionObject = {'field': jField, 'position': position, 'parent': parent};
            fieldsByPosition.push(positionObject);
        });

        /* Listeners */
        //Scroll
        var _window = $(window);
        var viewportHeight = $(window).height();
        _window.scroll(function ()
        {
            var windowScroll = _window.scrollTop();
            var lowerVisibilityBound = windowScroll + (viewportHeight / 3);
            var sectionToExpand = null;
            $.each(fieldsByPosition, function (index, fieldObject)
            {
                //Unfortunately, it seems that the position for all objects has to be recalculated on each scroll event
                var position = fieldObject['field'].offset().top;
                if (fieldObject['parent'].hasClass('collapsed')) {
                    position = fieldObject['parent'].offset().top;
                }
                if (position > windowScroll && position < lowerVisibilityBound) {
                    /* Only one section can be expanded at a time. Expands only if the fields inside fail validation. */
                    if (fieldObject['field'][0].__validation && fieldObject['field'][0].__validation.validate() == false) {
                        sectionToExpand == null ? sectionToExpand = fieldObject['parent'] : null;
                    }
                }
            });
            if (sectionToExpand) {
                sectionToExpand.removeClass('collapsed');
            }
        });

        //In case somebody decides to resize the window, and throw off visibility calculations
        _window.resize(function ()
        {
            viewportHeight = $(window).height();
        });

        saveButtons.on("click", function(){
            var numberMissing = 0;
            $.each(fieldsByPosition, function (index, fieldObject)
            {
                if (fieldObject['field'][0].__validation && !fieldObject['field'][0].__validation.validate()) {
                    fieldObject['parent'].removeClass('collapsed');
                    var position = fieldObject['field'].offset().top - (viewportHeight / 3);
                    position < 0 ? position = 0 : null;
                    _window.scrollTop(position);
                    numberMissing++;
                }
            });
            if (numberMissing > 0) {
                var dialog = new PhenoTips.widgets.ModalPopup("
Some fields are missing or invalid. Please review and correct the entered data. Missing or invalid fields: " + numberMissing + "
",
                    false, {'title': 'Invalid input', 'verticalPosition': 'top', 'removeOnClose': true});
                dialog.showDialog();
                document.fire("phenotips:scrollValidation:invalid");
            }
        });

        var addNewElement = function(field) {
            var jField = $(field);
            var position = jField.offset().top;
            var parent = $(jField.parents('.chapter')[0]);
            //Position could be 0, so in that case take the position of the parent section
            if (position == 0) {
                position = parent.offset().top;
            }
            var positionObject = {'field': jField, 'position': position, 'parent': parent};
            fieldsByPosition.push(positionObject);
        };

        document.observe('xwiki:dom:updated', function (event) {
            ((event && event.memo.elements) || [$('body')]).each(function(element) {
                element.select(".mandatory :input:not([type='hidden'])").each(function(field) {
                    addNewElement(field);
                });
                element.select(".checked :input:not([type='hidden'])").each(function(field) {
                    addNewElement(field);
                });
                // Also validate any date elements.
                element.select(".mandatory-date input.fuzzy-date").each(function(field) {
                  addNewElement(field);
                });
                element.select(".checked-date input.fuzzy-date").each(function(field) {
                  addNewElement(field);
                });
            });
        });
    });
});
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var igv = (function (igv) {

    const MAX_GZIP_BLOCK_SIZE = (1 << 16);

    /**
     * Reader for "bed like" files (tab delimited files with 1 feature per line: bed, gff, vcf, etc)
     *
     * @param config
     * @constructor
     */
    igv.FeatureFileReader = function (config) {

        var uriParts;

        this.config = config || {};
        this.indexURL = config.indexURL;
        this.indexed = config.indexed;

        if (igv.isFilePath(this.config.url)) {
            this.filename = this.config.url.name;
        } else if (this.config.url.startsWith('data:')) {
            this.indexed = false;  // by definition
            this.dataURI = config.url;
        } else {
            uriParts = igv.parseUri(this.config.url);
            this.filename = uriParts.file;
            this.path = uriParts.path;
        }

        this.format = this.config.format;

        this.parser = this.getParser(this.format, this.config.decode);

        this.supportsWholeGenome = (this.format === "seg");  // TODO -- move this up to track level
    };

    /**
     * Return a promise to load features for the genomic interval
     * @param chr
     * @param start
     * @param end
     */
    igv.FeatureFileReader.prototype.readFeatures = function (chr, start, end) {

        var self = this;

        return self.getIndex()
            .then(function (index) {
                if (index) {
                    return self.loadFeaturesWithIndex(chr, start, end);
                } else if (self.dataURI) {
                    return self.loadFeaturesFromDataURI();
                } else {
                    return self.loadFeaturesNoIndex()
                }
            });
    };

    igv.FeatureFileReader.prototype.readHeader = function () {

        var self = this;

        if (self.header) {
            return Promise.resolve(self.header);
        } else {
            return self.getIndex()
                .then(function (index) {

                    var options,
                        success;
                    if (self.dataURI) {
                        return self.loadFeaturesFromDataURI(self.dataURI)
                            .then(function (features) {
                                var header = self.header || {};
                                header.features = features;
                                return header;
                            })
                    } else if (index) {

                        // Load the file header (not HTTP header) for an indexed file.
                        // TODO -- note this will fail if the file header is > 65kb in size
                        options = igv.buildOptions(self.config, {bgz: index.tabix, range: {start: 0, size: 65000}});

                        return igv.xhr.loadString(self.config.url, options)
                            .then(function (data) {
                                self.header = self.parser.parseHeader(data);
                                return self.header
                            });
                    } else {
                        // If this is a non-indexed file we will load all features in advance
                        return self.loadFeaturesNoIndex()
                            .then(function (features) {
                                var header = self.header || {};
                                header.features = features;
                                return header;
                            })
                    }
                })
        }

    };

    igv.FeatureFileReader.prototype.getParser = function (format, decode) {

        switch (format) {
            case "vcf":
                return new igv.VcfParser();
            case "seg" :
                return new igv.SegParser();
            default:
                return new igv.FeatureParser(format, decode, this.config);
        }

    };

    /**
     * Return a Promise for the async loaded index
     */
    igv.FeatureFileReader.prototype.loadIndex = function () {
        var idxFile = this.config.indexURL;
        if (this.filename.endsWith('.gz')) {
            if (!idxFile) {
                idxFile = this.config.url + '.tbi';
            }
            return igv.loadBamIndex(idxFile, this.config, true);
        } else {
            if (!idxFile) {
                idxFile = this.config.url + '.idx';
            }
            return igv.loadTribbleIndex(idxFile, this.config);
        }
    };

    igv.FeatureFileReader.prototype.loadFeaturesNoIndex = function () {

        var self = this;

        var options = igv.buildOptions(self.config);    // Add oauth token, if any

        return igv.xhr.loadString(self.config.url, options)
            .then(function (data) {
                self.header = self.parser.parseHeader(data);
                if (self.header instanceof String && self.header.startsWith("##gff-version 3")) {
                    self.format = 'gff3';
                }
                return self.parser.parseFeatures(data);   // <= PARSING DONE HERE
            })
            // .catch(function(error) {
            //     console.log(error);
            // })
    };

    igv.FeatureFileReader.prototype.loadFeaturesWithIndex = function (chr, start, end) {

        //console.log("Using index");
        var self = this;

        var blocks,
            tabix = self.index && self.index.tabix,
            refId = tabix ? self.index.sequenceIndexMap[chr] : chr,
            promises = [];

        blocks = self.index.blocksForRange(refId, start, end);

        if (!blocks || blocks.length === 0) {
            return Promise.resolve([]);
        } else {

            blocks.forEach(function (block) {

                promises.push(new Promise(function (fullfill, reject) {

                    var startPos = block.minv.block,
                        startOffset = block.minv.offset,
                        endPos,
                        options,
                        success;

                    endPos = block.maxv.block + MAX_GZIP_BLOCK_SIZE;

                    options = igv.buildOptions(self.config, {
                        range: {
                            start: startPos,
                            size: endPos - startPos + 1
                        }
                    });

                    success = function (data) {

                        var inflated,
                            slicedData,
                            slicedFeatures,
                            filteredFeatures,
                            f,
                            i;

                        if (self.index.tabix) {
                            inflated = new Uint8Array(igv.unbgzf(data));
                        } else {
                            inflated = data;
                        }

                        slicedData = startOffset ? inflated.slice(startOffset) : inflated;
                        slicedFeatures = self.parser.parseFeatures(slicedData);

                        // Filter features not in requested range.
                        filteredFeatures = [];
                        for (i = 0; i < slicedFeatures.length; i++) {
                            f = slicedFeatures[i];
                            if (f.start > end) break;
                            if (f.end >= start && f.start <= end) {
                                filteredFeatures.push(f);
                            }
                        }

                        fullfill(filteredFeatures);
                    };

                    // Async load
                    if (self.index.tabix) {
                        igv.xhr
                            .loadArrayBuffer(self.config.url, options)
                            .then(success)
                            .catch(reject);
                    } else {
                        igv.xhr
                            .loadString(self.config.url, options)
                            .then(success)
                            .catch(reject);
                    }

                }))
            });

            return Promise.all(promises)

                .then(function (featureArrays) {

                    var i,
                        allFeatures;

                    if (featureArrays.length === 1) {
                        allFeatures = featureArrays[0];
                    } else {
                        allFeatures = featureArrays[0];

                        for (i = 1; i < featureArrays.length; i++) {
                            allFeatures = allFeatures.concat(featureArrays[i]);
                        }

                        allFeatures.sort(function (a, b) {
                            return a.start - b.start;
                        });
                    }

                    return allFeatures;
                })
        }

    };

    igv.FeatureFileReader.prototype.getIndex = function () {

        var self = this;
        if (self.index !== undefined || self.indexed === false) {
            return Promise.resolve(self.index);
        }

        if (self.indexURL || self.indexed || (typeof self.config.url === 'string' && self.config.url.endsWith(".gz"))) {

            return self.loadIndex()

                .then(function (indexOrUndefined) {
                    if (indexOrUndefined) {
                        self.index = indexOrUndefined;
                        self.indexed = true;
                    } else {
                        self.indexed = false;
                    }
                    return self.index;
                })
                .catch(function (error) {
                    self.indexed = false;
                    if (error.message === '404' && self.config.indexURL === undefined) {
                        igv.presentAlert("Index file not found.  Check track configuration", undefined)
                    } else {
                        throw error;
                    }
                });
        } else {
            self.indexed = false;
            return Promise.resolve(undefined);
        }
    };

    igv.FeatureFileReader.prototype.loadFeaturesFromDataURI = function () {
        var bytes, inflate, plain, features,
            split = this.dataURI.split(','),
            info = split[0].split(':')[1],
            dataString = split[1];

        if (info.indexOf('base64') >= 0) {
            dataString = atob(dataString);
        } else {
            dataString = decodeURI(dataString);
        }

        bytes = new Uint8Array(dataString.length);
        for (var i = 0; i < dataString.length; i++) {
            bytes[i] = dataString.charCodeAt(i);
        }

        inflate = new Zlib.Gunzip(bytes);
        plain = inflate.decompress();
        features = this.parser.parseFeatures(plain);
        return Promise.resolve(features);
    };

    return igv;
})
(igv || {});
<commit_msg>Fix handling of optional index load error<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var igv = (function (igv) {

    const MAX_GZIP_BLOCK_SIZE = (1 << 16);

    /**
     * Reader for "bed like" files (tab delimited files with 1 feature per line: bed, gff, vcf, etc)
     *
     * @param config
     * @constructor
     */
    igv.FeatureFileReader = function (config) {

        var uriParts;

        this.config = config || {};
        this.indexURL = config.indexURL;
        this.indexed = config.indexed;

        if (igv.isFilePath(this.config.url)) {
            this.filename = this.config.url.name;
        } else if (this.config.url.startsWith('data:')) {
            this.indexed = false;  // by definition
            this.dataURI = config.url;
        } else {
            uriParts = igv.parseUri(this.config.url);
            this.filename = uriParts.file;
            this.path = uriParts.path;
        }

        this.format = this.config.format;

        this.parser = this.getParser(this.format, this.config.decode);

        this.supportsWholeGenome = (this.format === "seg");  // TODO -- move this up to track level
    };

    /**
     * Return a promise to load features for the genomic interval
     * @param chr
     * @param start
     * @param end
     */
    igv.FeatureFileReader.prototype.readFeatures = function (chr, start, end) {

        var self = this;

        return self.getIndex()
            .then(function (index) {
                if (index) {
                    return self.loadFeaturesWithIndex(chr, start, end);
                } else if (self.dataURI) {
                    return self.loadFeaturesFromDataURI();
                } else {
                    return self.loadFeaturesNoIndex()
                }
            });
    };

    igv.FeatureFileReader.prototype.readHeader = function () {

        var self = this;

        if (self.header) {
            return Promise.resolve(self.header);
        } else {
            return self.getIndex()
                .then(function (index) {

                    var options,
                        success;
                    if (self.dataURI) {
                        return self.loadFeaturesFromDataURI(self.dataURI)
                            .then(function (features) {
                                var header = self.header || {};
                                header.features = features;
                                return header;
                            })
                    } else if (index) {

                        // Load the file header (not HTTP header) for an indexed file.
                        // TODO -- note this will fail if the file header is > 65kb in size
                        options = igv.buildOptions(self.config, {bgz: index.tabix, range: {start: 0, size: 65000}});

                        return igv.xhr.loadString(self.config.url, options)
                            .then(function (data) {
                                self.header = self.parser.parseHeader(data);
                                return self.header
                            });
                    } else {
                        // If this is a non-indexed file we will load all features in advance
                        return self.loadFeaturesNoIndex()
                            .then(function (features) {
                                var header = self.header || {};
                                header.features = features;
                                return header;
                            })
                    }
                })
        }

    };

    igv.FeatureFileReader.prototype.getParser = function (format, decode) {

        switch (format) {
            case "vcf":
                return new igv.VcfParser();
            case "seg" :
                return new igv.SegParser();
            default:
                return new igv.FeatureParser(format, decode, this.config);
        }

    };

    /**
     * Return a Promise for the async loaded index
     */
    igv.FeatureFileReader.prototype.loadIndex = function () {
        var idxFile = this.config.indexURL;
        if (this.filename.endsWith('.gz')) {
            if (!idxFile) {
                idxFile = this.config.url + '.tbi';
            }
            return igv.loadBamIndex(idxFile, this.config, true);
        } else {
            if (!idxFile) {
                idxFile = this.config.url + '.idx';
            }
            return igv.loadTribbleIndex(idxFile, this.config);
        }
    };

    igv.FeatureFileReader.prototype.loadFeaturesNoIndex = function () {

        var self = this;

        var options = igv.buildOptions(self.config);    // Add oauth token, if any

        return igv.xhr.loadString(self.config.url, options)
            .then(function (data) {
                self.header = self.parser.parseHeader(data);
                if (self.header instanceof String && self.header.startsWith("##gff-version 3")) {
                    self.format = 'gff3';
                }
                return self.parser.parseFeatures(data);   // <= PARSING DONE HERE
            })
            // .catch(function(error) {
            //     console.log(error);
            // })
    };

    igv.FeatureFileReader.prototype.loadFeaturesWithIndex = function (chr, start, end) {

        //console.log("Using index");
        var self = this;

        var blocks,
            tabix = self.index && self.index.tabix,
            refId = tabix ? self.index.sequenceIndexMap[chr] : chr,
            promises = [];

        blocks = self.index.blocksForRange(refId, start, end);

        if (!blocks || blocks.length === 0) {
            return Promise.resolve([]);
        } else {

            blocks.forEach(function (block) {

                promises.push(new Promise(function (fullfill, reject) {

                    var startPos = block.minv.block,
                        startOffset = block.minv.offset,
                        endPos,
                        options,
                        success;

                    endPos = block.maxv.block + MAX_GZIP_BLOCK_SIZE;

                    options = igv.buildOptions(self.config, {
                        range: {
                            start: startPos,
                            size: endPos - startPos + 1
                        }
                    });

                    success = function (data) {

                        var inflated,
                            slicedData,
                            slicedFeatures,
                            filteredFeatures,
                            f,
                            i;

                        if (self.index.tabix) {
                            inflated = new Uint8Array(igv.unbgzf(data));
                        } else {
                            inflated = data;
                        }

                        slicedData = startOffset ? inflated.slice(startOffset) : inflated;
                        slicedFeatures = self.parser.parseFeatures(slicedData);

                        // Filter features not in requested range.
                        filteredFeatures = [];
                        for (i = 0; i < slicedFeatures.length; i++) {
                            f = slicedFeatures[i];
                            if (f.start > end) break;
                            if (f.end >= start && f.start <= end) {
                                filteredFeatures.push(f);
                            }
                        }

                        fullfill(filteredFeatures);
                    };

                    // Async load
                    if (self.index.tabix) {
                        igv.xhr
                            .loadArrayBuffer(self.config.url, options)
                            .then(success)
                            .catch(reject);
                    } else {
                        igv.xhr
                            .loadString(self.config.url, options)
                            .then(success)
                            .catch(reject);
                    }

                }))
            });

            return Promise.all(promises)

                .then(function (featureArrays) {

                    var i,
                        allFeatures;

                    if (featureArrays.length === 1) {
                        allFeatures = featureArrays[0];
                    } else {
                        allFeatures = featureArrays[0];

                        for (i = 1; i < featureArrays.length; i++) {
                            allFeatures = allFeatures.concat(featureArrays[i]);
                        }

                        allFeatures.sort(function (a, b) {
                            return a.start - b.start;
                        });
                    }

                    return allFeatures;
                })
        }

    };

    igv.FeatureFileReader.prototype.getIndex = function () {

        var self = this;
        if (self.index !== undefined || self.indexed === false) {
            return Promise.resolve(self.index);
        }

        if (self.indexURL || self.indexed || (typeof self.config.url === 'string' && self.config.url.endsWith(".gz"))) {

            return self.loadIndex()

                .then(function (indexOrUndefined) {
                    if (indexOrUndefined) {
                        self.index = indexOrUndefined;
                        self.indexed = true;
                    } else {
                        self.indexed = false;
                    }
                    return self.index;
                })
                .catch(function (error) {
                    self.indexed = false;
                    if (self.config.indexURL !== undefined) {
                        igv.presentAlert("Index file not found.  Check track configuration", undefined)
                    }
                });
        } else {
            self.indexed = false;
            return Promise.resolve(undefined);
        }
    };

    igv.FeatureFileReader.prototype.loadFeaturesFromDataURI = function () {
        var bytes, inflate, plain, features,
            split = this.dataURI.split(','),
            info = split[0].split(':')[1],
            dataString = split[1];

        if (info.indexOf('base64') >= 0) {
            dataString = atob(dataString);
        } else {
            dataString = decodeURI(dataString);
        }

        bytes = new Uint8Array(dataString.length);
        for (var i = 0; i < dataString.length; i++) {
            bytes[i] = dataString.charCodeAt(i);
        }

        inflate = new Zlib.Gunzip(bytes);
        plain = inflate.decompress();
        features = this.parser.parseFeatures(plain);
        return Promise.resolve(features);
    };

    return igv;
})
(igv || {});
<|endoftext|>


<commit_before>var webRemix = require('../lib/web-remix');
var nock = require('nock');
var should = require('should');

describe('web-remix', function() {
  describe('.generate',  function() {

    it('returns embed code for a youtu.be short url', function(done) {
      webRemix.generate('http://youtu.be/5cazkHAHiPU', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a youtube normal url', function(done) {
      webRemix.generate('http://www.youtube.com/watch?v=5cazkHAHiPU', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a vimeo video url', function(done) {
      webRemix.generate('http://vimeo.com/37872583', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a mixcloud audio url', function(done) {
      webRemix.generate('http://www.mixcloud.com/LuckyMe/25-jamie-vexd-sunday-walkman-mix/', function(err, subject) {
        subject.should.equal(' <object width="470" height="268"><param name="movie" value="http://www.mixcloud.' +
          'com/media/swf/player/mixcloudLoader.swf?feed=http%3A//www.mixcloud.com/LuckyMe/25-jamie-vexd-sunday-' +
          'walkman-mix/&embed_type=widget_standard"></param><param name="allowFullScreen" value="false"></param>' +
          '<param name="wmode" value="opaque"></param><param name="allowscriptaccess" value="always"></param>' +
          '<embed src="//www.mixcloud.com/media/swf/player/mixcloudLoader.swf?feed=http%3A//www.mixcloud.com' +
          '/LuckyMe/25-jamie-vexd-sunday-walkman-mix/&embed_type=widget_standard" type="application/x-shockwave-' +
          'flash" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" width="470" height="268"></embed>' +
          '</object>

');
        done();
      });
    });

    it('returns oembed code for a soundcloud url', function(done) {
      var scope = nock('soundcloud.com').get('/oembed?format=json&url=http//soundcloud.com/track').reply(200,
          { html: 'http://soundcloud.com/skeptical/sets' +
          '/tracks-576/' });
      webRemix.generate('http://soundcloud.com/skeptical/sets/tracks-576/', function(err, subject) {
        subject.should.equal(' ' +
          '');
        done();
      });
    });

    it('returns embed code for a rd.io short url', function(done) {
      webRemix.generate('http://rd.io/i/QVME9DdeW1GL', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a rdio normal url', function(done) {
      webRemix.generate('http://rdio.com/x/QVME9DdeW1GL', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns image code for an img url', function() {
      webRemix.generate('http://3.bp.blogspot.com/_K_1LxF4TvhU/S7UUE6PYKiI/AAAAAAAADto/XfpdX2CIxqY/' +
        's400/Riley+the+smiling+dog.jpg', function(err, subject) {
        subject.should.equal(' ');
      });
    });

    it('returns a link for an https url', function(done) {
      webRemix.generate('https://example.com', function(err, subject) {
        subject.should.equal(' https://example.com');
        done();
      });
    });

    it('returns a link for an http url', function(done) {
      webRemix.generate('http://example.com', function(err, subject) {
        subject.should.equal(' http://example.com');
        done();
      });
    });

    it('returns video for a video link', function(done) {
      var video = 'http://blah.com/video.ogv';
      webRemix.generate(video, function(err, subject) {
        subject.should.equal(' <video controls="controls" preload="none" autobuffer><source src="' + video +
          '" type="video/ogg; codecs="vp8, vorbis" /></video>');
        done();
      });
    });

    it('returns audio for an audio link', function(done) {
      var audio = 'http://blah.com/audio.ogg';
      webRemix.generate(audio, function(err, subject) {
        subject.should.equal(' <audio controls="controls" preload="none" autobuffer><source src="' + audio +
          '" type="audio/ogg" /></audio>');
        done();
      });
    });

    it('returns the user link', function(done) {
      webRemix.generate('@borg', function(err, subject) {
        subject.should.equal(' @borg');
        done();
      });
    });

    it('returns a non-user link', function(done) {
      webRemix.generate('@ borg', function(err, subject) {
        subject.should.equal(' @ borg');
        done();
      });
    });

    it('returns the plain text for anything else', function(done) {
      webRemix.generate('foo', function(err, subject) {
        subject.should.equal(' foo');
        done();
      });
    });
  });
});
<commit_msg>fixed test<commit_after>var webRemix = require('../lib/web-remix');
var nock = require('nock');
var should = require('should');

describe('web-remix', function() {
  describe('.generate',  function() {

    it('returns embed code for a youtu.be short url', function(done) {
      webRemix.generate('http://youtu.be/5cazkHAHiPU', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a youtube normal url', function(done) {
      webRemix.generate('http://www.youtube.com/watch?v=5cazkHAHiPU', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a vimeo video url', function(done) {
      webRemix.generate('http://vimeo.com/37872583', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a mixcloud audio url', function(done) {
      webRemix.generate('http://www.mixcloud.com/LuckyMe/25-jamie-vexd-sunday-walkman-mix/', function(err, subject) {
        subject.should.equal(' <object width="470" height="268"><param name="movie" value="http://www.mixcloud.' +
          'com/media/swf/player/mixcloudLoader.swf?feed=http%3A//www.mixcloud.com/LuckyMe/25-jamie-vexd-sunday-' +
          'walkman-mix/&embed_type=widget_standard"></param><param name="allowFullScreen" value="false"></param>' +
          '<param name="wmode" value="opaque"></param><param name="allowscriptaccess" value="always"></param>' +
          '<embed src="//www.mixcloud.com/media/swf/player/mixcloudLoader.swf?feed=http%3A//www.mixcloud.com' +
          '/LuckyMe/25-jamie-vexd-sunday-walkman-mix/&embed_type=widget_standard" type="application/x-shockwave-' +
          'flash" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" width="470" height="268"></embed>' +
          '</object>

');
        done();
      });
    });

    it('returns oembed code for a soundcloud url', function(done) {
      var scope = nock('soundcloud.com').get('/oembed?format=json&url=http//soundcloud.com/track').reply(200,
          { html: 'http://soundcloud.com/skeptical/sets' +
          '/tracks-576/' });
      webRemix.generate('http://soundcloud.com/skeptical/sets/tracks-576/', function(err, subject) {
        subject.should.equal(' ' +
          '');
        done();
      });
    });

    it('returns embed code for a rd.io short url', function(done) {
      webRemix.generate('http://rd.io/i/QVME9DdeW1GL', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns embed code for a rdio normal url', function(done) {
      webRemix.generate('http://rdio.com/x/QVME9DdeW1GL', function(err, subject) {
        subject.should.equal(' ');
        done();
      });
    });

    it('returns image code for an img url', function() {
      webRemix.generate('http://3.bp.blogspot.com/_K_1LxF4TvhU/S7UUE6PYKiI/AAAAAAAADto/XfpdX2CIxqY/' +
        's400/Riley+the+smiling+dog.jpg', function(err, subject) {
        subject.should.equal('

');
      });
    });

    it('returns a link for an https url', function(done) {
      webRemix.generate('https://example.com', function(err, subject) {
        subject.should.equal(' https://example.com');
        done();
      });
    });

    it('returns a link for an http url', function(done) {
      webRemix.generate('http://example.com', function(err, subject) {
        subject.should.equal(' http://example.com');
        done();
      });
    });

    it('returns video for a video link', function(done) {
      var video = 'http://blah.com/video.ogv';
      webRemix.generate(video, function(err, subject) {
        subject.should.equal(' <video controls="controls" preload="none" autobuffer><source src="' + video +
          '" type="video/ogg; codecs="vp8, vorbis" /></video>');
        done();
      });
    });

    it('returns audio for an audio link', function(done) {
      var audio = 'http://blah.com/audio.ogg';
      webRemix.generate(audio, function(err, subject) {
        subject.should.equal(' <audio controls="controls" preload="none" autobuffer><source src="' + audio +
          '" type="audio/ogg" /></audio>');
        done();
      });
    });

    it('returns the user link', function(done) {
      webRemix.generate('@borg', function(err, subject) {
        subject.should.equal(' @borg');
        done();
      });
    });

    it('returns a non-user link', function(done) {
      webRemix.generate('@ borg', function(err, subject) {
        subject.should.equal(' @ borg');
        done();
      });
    });

    it('returns the plain text for anything else', function(done) {
      webRemix.generate('foo', function(err, subject) {
        subject.should.equal(' foo');
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Tests for init system for Node.js.
 * (C) 2015 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var testing = require('testing');
var inits = require('../lib/initSystem.js');

// globals
var log = new Log('info');

function testInitSystem(callback)
{
	var system = new inits.InitSystem();
	var witness = {};
	system.on('error', function(error)
	{
		testing.failure(error, callback);
	});
	system.init(function(next)
	{
		witness.init = true;
		log.debug('init');
		next(null);
	});
	system.start(function(next)
	{
		testing.assert(witness.init, 'Should call init', callback);
		witness.start = true;
		log.debug('start');
		next(null);
	});
	system.on('ready', function()
	{
		testing.assert(witness.start, 'Should call start', callback);
		system.shutdown();
	});
	system.stop(function(next)
	{
		testing.assert(witness.start, 'Should call start', callback);
		witness.stop = true;
		log.debug('stop');
		next(null);
	});
	system.finish(function(next)
	{
		testing.assert(witness.stop, 'Should call stop', callback);
		witness.finish = true;
		log.debug('finish');
		next(null);
	});
	system.on('end', function()
	{
		testing.assert(witness.finish, 'Should call finish', callback);
		testing.success(callback);
	});
}

function testStandalone(callback)
{
	var system = new inits.InitSystem();
	var witness = {};
	system.on('error', function(error)
	{
		testing.failure(error, callback);
	});
	system.init(function(next)
	{
		witness.init = true;
		log.debug('init');
		next(null);
	});
	system.standalone(function(next)
	{
		testing.assert(witness.init, 'Should call init', callback);
		witness.standalone = true;
		log.debug('standalone');
		next(null);
	});
	system.finish(function(next)
	{
		testing.assert(witness.standalone, 'Should call standalone', callback);
		witness.finish = true;
		log.debug('finish');
		next(null);
	});
	system.on('end', function()
	{
		testing.assert(witness.finish, 'Should call finish', callback);
		testing.success(callback);
	});
}

function testSeveralCallbacks(callback)
{
	var system = new inits.InitSystem();
	var witness = {};
	system.on('error', function(error)
	{
		testing.failure(error, callback);
	});
	system.init(function(next)
	{
		witness.init1 = true;
		log.debug('init1');
		next(null);
	});
	system.init(function(next)
	{
		witness.init2 = true;
		log.debug('init2');
		next(null);
	});
	system.standalone(function(next)
	{
		testing.assert(witness.init1, 'Should call init1', callback);
		testing.assert(witness.init2, 'Should call init2', callback);
		witness.standalone = true;
		log.debug('standalone');
		next();
	});
	system.finish(function(next)
	{
		witness.finish1 = true;
		log.debug('finish1');
		next(null);
	});
	system.finish(function(next)
	{
		witness.finish2 = true;
		log.debug('finish2');
		next(null);
	});
	system.on('end', function()
	{
		testing.assert(witness.finish1, 'Should call finish1', callback);
		testing.assert(witness.finish2, 'Should call finish2', callback);
		testing.success(callback);
	});
}

function testExceptions(callback)
{
	var system = new inits.InitSystem();
	system.options.showErrors = false;
	var init1 = false;
	system.on('error', function(error)
	{
		if (error.contains('init1'))
		{
			init1 = true;
			return;
		}
		else if (error.contains('finish1'))
		{
			testing.assert(init1, 'Should catch init1 error', callback);
			testing.success(callback);
		}
		else
		{
			testing.failure('Invalid error ' + error);
		}
	});
	system.init(function(next)
	{
		next('init1');
	});
	system.init(function(next)
	{
		next('init2');
	});
	system.start(function(next)
	{
		next('start');
	});
	system.finish(function(next)
	{
		next('finish1');
	});
	system.finish(function(next)
	{
		next('finish2');
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	testing.run([
		testInitSystem,
		testStandalone,
		testSeveralCallbacks,
		testExceptions,
	], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	log = new Log('debug');
	inits.standalone(testing.toShow(exports.test));
}

<commit_msg>Do not exit in tests.<commit_after>'use strict';

/**
 * Tests for init system for Node.js.
 * (C) 2015 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var testing = require('testing');
var inits = require('../lib/initSystem.js');

// globals
var log = new Log('info');

function testInitSystem(callback)
{
	var system = new inits.InitSystem();
	system.options.exitProcess = false;
	var witness = {};
	system.on('error', function(error)
	{
		testing.failure(error, callback);
	});
	system.init(function(next)
	{
		witness.init = true;
		log.debug('init');
		next(null);
	});
	system.start(function(next)
	{
		testing.assert(witness.init, 'Should call init', callback);
		witness.start = true;
		log.debug('start');
		next(null);
	});
	system.on('ready', function()
	{
		testing.assert(witness.start, 'Should call start', callback);
		system.shutdown();
	});
	system.stop(function(next)
	{
		testing.assert(witness.start, 'Should call start', callback);
		witness.stop = true;
		log.debug('stop');
		next(null);
	});
	system.finish(function(next)
	{
		testing.assert(witness.stop, 'Should call stop', callback);
		witness.finish = true;
		log.debug('finish');
		next(null);
	});
	system.on('end', function()
	{
		testing.assert(witness.finish, 'Should call finish', callback);
		testing.success(callback);
	});
}

function testStandalone(callback)
{
	var system = new inits.InitSystem();
	system.options.exitProcess = false;
	var witness = {};
	system.on('error', function(error)
	{
		testing.failure(error, callback);
	});
	system.init(function(next)
	{
		witness.init = true;
		log.debug('init');
		next(null);
	});
	system.standalone(function(next)
	{
		testing.assert(witness.init, 'Should call init', callback);
		witness.standalone = true;
		log.debug('standalone');
		next(null);
	});
	system.finish(function(next)
	{
		testing.assert(witness.standalone, 'Should call standalone', callback);
		witness.finish = true;
		log.debug('finish');
		next(null);
	});
	system.on('end', function()
	{
		testing.assert(witness.finish, 'Should call finish', callback);
		testing.success(callback);
	});
}

function testSeveralCallbacks(callback)
{
	var system = new inits.InitSystem();
	system.options.exitProcess = false;
	var witness = {};
	system.on('error', function(error)
	{
		testing.failure(error, callback);
	});
	system.init(function(next)
	{
		witness.init1 = true;
		log.debug('init1');
		next(null);
	});
	system.init(function(next)
	{
		witness.init2 = true;
		log.debug('init2');
		next(null);
	});
	system.standalone(function(next)
	{
		testing.assert(witness.init1, 'Should call init1', callback);
		testing.assert(witness.init2, 'Should call init2', callback);
		witness.standalone = true;
		log.debug('standalone');
		next();
	});
	system.finish(function(next)
	{
		witness.finish1 = true;
		log.debug('finish1');
		next(null);
	});
	system.finish(function(next)
	{
		witness.finish2 = true;
		log.debug('finish2');
		next(null);
	});
	system.on('end', function()
	{
		testing.assert(witness.finish1, 'Should call finish1', callback);
		testing.assert(witness.finish2, 'Should call finish2', callback);
		testing.success(callback);
	});
}

function testExceptions(callback)
{
	var system = new inits.InitSystem();
	system.options.exitProcess = false;
	system.options.showErrors = false;
	var init1 = false;
	system.on('error', function(error)
	{
		if (error.contains('init1'))
		{
			init1 = true;
			return;
		}
		else if (error.contains('finish1'))
		{
			testing.assert(init1, 'Should catch init1 error', callback);
			testing.success(callback);
		}
		else
		{
			testing.failure('Invalid error ' + error);
		}
	});
	system.init(function(next)
	{
		next('init1');
	});
	system.init(function(next)
	{
		next('init2');
	});
	system.start(function(next)
	{
		next('start');
	});
	system.finish(function(next)
	{
		next('finish1');
	});
	system.finish(function(next)
	{
		next('finish2');
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	testing.run([
		testInitSystem,
		testStandalone,
		testSeveralCallbacks,
		testExceptions,
	], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	log = new Log('debug');
	inits.standalone(testing.toShow(exports.test));
}

<|endoftext|>