<commit_before>/* globals controlplane: true */

/* AppsController
 * Displays top level apps
 */
(function() {
    'use strict';

    controlplane.controller("AppsController", [
        "$scope", "$routeParams", "$location",
        "$notification", "resourcesFactory", "authService",
        "$modalService", "$translate", "$timeout",
        "$cookies", "servicesFactory", "miscUtils",
        "ngTableParams", "$filter",
    function($scope, $routeParams, $location, $notification, resourcesFactory, authService, $modalService, $translate, $timeout, $cookies, servicesFactory, utils, NgTableParams, $filter){
        // Ensure logged in
        authService.checkLogin($scope);

        // alias hostname instead of using localhost or IP
        $scope.defaultHostAlias = $location.host();

        // redirect to specific service details
        $scope.routeToService = function(id) {
            resourcesFactory.routeToService(id);
        };

        // redirect to specific pool
        $scope.routeToPool = function(id) {
            resourcesFactory.routeToPool(id);
        };

        $scope.modal_deployWizard = function() {
            // the modal occasionally won't show on page load, so we use a timeout to get around that.
            // TODO - use a separate controller for deploy wizard
            $timeout(function(){
                $('#addApp').modal('show');

                // don't auto-show this wizard again
                // NOTE: $cookies can only deal with string values
                $cookies.autoRunWizardHasRun = "true";
            });
        };

        $scope.modal_addTemplate = function() {
            $modalService.create({
                templateUrl: "add-template.html",
                model: $scope,
                title: "template_add",
                actions: [
                    {
                        role: "cancel",
                        action: function(){
                            $scope.newHost = {};
                            this.close();
                        }
                    },{
                        role: "ok",
                        label: "template_add",
                        action: function(){
                            if(this.validate()){
                                var data = new FormData();

                                $.each($("#new_template_filename")[0].files, function(key, value){
                                    data.append("tpl", value);
                                });

                                // disable ok button, and store the re-enable function
                                var enableSubmit = this.disableSubmitButton();

                                resourcesFactory.addAppTemplate(data)
                                    .success(function(data){
                                        $notification.create("Added template", data.Detail).success();
                                        refreshTemplates();
                                        this.close();
                                    }.bind(this))
                                    .error(function(data){
                                        this.createNotification("Adding template failed", data.Detail).error();
                                        enableSubmit();
                                    }.bind(this));
                            }
                        }
                    }
                ]
            });
        };

        // aggregate vhosts for a specified service, but
        // only if the service has changed since last request
        $scope.aggregateVHosts = utils.memoize(function(service) {
            var vHosts = [];

            service.model.Endpoints.forEach(endpoint => {
                if(endpoint.VHostList){
                    endpoint.VHostList.forEach(vHost => vHosts.push(vHost));
                }
            });

            vHosts.sort();

            return vHosts;
        }, function(service){
            return service.id + service.model.DatabaseVersion;
        });

        // given a vhost, return a url to it
        $scope.createVHostURL = function(vhost) {
            var port = $location.port() === "" ? "" : ":"+$location.port();
            var host = vhost.Name.indexOf('.') === -1 ? vhost.Name + "." + $scope.defaultHostAlias : vhost.Name;
            return $location.protocol() + "://" + host + port;
        };

        $scope.modal_removeService = function(service) {
            $modalService.create({
                template: $translate.instant("warning_remove_service"),
                model: $scope,
                title: "remove_service",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "remove_service",
                        classes: "btn-danger submit",
                        action: function(){
                            if(this.validate()){
                                this.disableSubmitButton();

                                removeService(service)
                                    .success(() => {
                                        $notification.create("Removed App", service.name).success();
                                        this.close();
                                    })
                                    .error((data, status) => {
                                        $notification.create("Remove App failed", data.Detail).error();
                                        this.close();
                                    });
                            }
                        }
                    }
                ]
            });
        };

        $scope.startService = function(service){
            $scope.modal_startStopService(service, "start");
        };
        $scope.stopService = function(service){
            $scope.modal_startStopService(service, "stop");
        };
        $scope.modal_startStopService = function(service, status) {
            var displayStatus = utils.capitalizeFirst(status);

            $modalService.create({
                template: $translate.instant("confirm_"+ status +"_app"),
                model: $scope,
                title: displayStatus +" Services",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: displayStatus +" Services",
                        action: function(){
                            service[status]();
                            this.close();
                        }
                    }
                ]
            });
        };

        $scope.clickRunningApp = function(service, status) {
            utils.setServiceState($scope, service, status, $modalService, $translate);
        };

        $scope.modal_deleteTemplate = function(templateID){
            $modalService.create({
                template: $translate.instant("template_remove_confirm") + "<strong>"+ templateID +"</strong>",
                model: $scope,
                title: "template_remove",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "template_remove",
                        classes: "btn-danger",
                        action: function(){
                            deleteTemplate(templateID)
                                .success(() => {
                                    $notification.create("Removed Template", templateID).success();
                                    this.close();
                                })
                                .error((data, status) => {
                                    $notification.create("Remove Template failed", data.Detail).error();
                                });
                        }
                    }
                ]
            });
        };

        /*
         * PRIVATE FUNCTIONS
         */
        function refreshTemplates(){
            resourcesFactory.getAppTemplates().success(function(templates) {
                $scope.templates.data = utils.mapToArr(templates);
            });
        }

        // poll for apps that are being deployed
        $scope.deployingServices = [];
        var lastPollResults = 0;
        function getDeploying(){
            resourcesFactory.getDeployingTemplates().success(function(data) {
                if(data){
                    $scope.deployingServices = data;
                }

                //if we have fewer results than last poll, we need to refresh our table
                //TODO - better checking for deploying apps
                if(lastPollResults > $scope.deployingServices.length){
                    servicesFactory.update();
                }
                lastPollResults = $scope.deployingServices.length;
            });
        }

        function removeService(service) {
            return resourcesFactory.removeService(service.id)
                .success(function(){
                    // NOTE: this is here because services are
                    // incrementally updated, which makes it impossible
                    // to determine if a service has been removed
                    // TODO - once the backend notifies on deleted
                    // services, this should be removed
                    // FIXME - should not modify servicesFactory's
                    // objects!
                    for(var i = 0; i < $scope.apps.length; i++){
                        // find the removed service and remove it
                        if($scope.apps[i].id === service.id){
                            $scope.apps.splice(i, 1);
                            return;
                        }
                    }
                });
        }

        function deleteTemplate(templateID){
            return resourcesFactory.removeAppTemplate(templateID)
                .success(refreshTemplates);
        }

        // init stuff for this controller
        function init(){
            if(utils.needsHostAlias($location.host())){
                resourcesFactory.getHostAlias().success(function(data) {
                    $scope.defaultHostAlias = data.hostalias;
                });
            }

            // configure tables
            // TODO - move table config to view/directive
            $scope.breadcrumbs = [
                { label: 'breadcrumb_deployed', itemClass: 'active' }
            ];

            $scope.servicesTable = {
                sorting: {
                    name: "asc"
                },
                getData: function(data, params) {
                    // use built-in angular filter
                    var orderedData = params.sorting() ?
                        $filter('orderBy')(data, params.orderBy()) :
                        data;

                    if(!orderedData){
                        return;
                    }

                    // mark any deploying services so they can be treated differently
                    orderedData.forEach(function(service){
                        service.deploying = false;
                        $scope.deployingServices.forEach(function(deploying){
                            if(service.model.DeploymentID === deploying.DeploymentID){
                                service.deploying = true;
                            }
                        });
                    });

                    return orderedData;
                },
                watchExpression: function(){
                    // TODO - check $scope.deployingServices as well
                    return servicesFactory.lastUpdate;
                }
            };

            $scope.templates = { data: [] };
            // table config
            $scope.templatesTable = {
                sorting: {
                    Name: "asc"
                }
            };

            // Get a list of templates
            refreshTemplates();

            // check for deploying apps
            getDeploying();

            // start polling for apps
            servicesFactory.activate();
            servicesFactory.update().then(function(){
                // locally bind top level service list
                $scope.apps = servicesFactory.serviceTree;

                // if only isvcs are deployed, and this is the first time
                // running deploy wizard, show the deploy apps modal
                if(!$cookies.autoRunWizardHasRun && $scope.apps.length === 1){
                    $scope.modal_deployWizard();
                }
            });

            //register polls
            resourcesFactory.registerPoll("deployingApps", getDeploying, 3000);
            resourcesFactory.registerPoll("templates", refreshTemplates, 5000);

            //unregister polls on destroy
            $scope.$on("$destroy", function(){
                resourcesFactory.unregisterAllPolls();
            });
        }

        // kick this controller off
        init();

        $scope.$on("$destroy", function(){
            resourcesFactory.unregisterAllPolls();
            servicesFactory.deactivate();
        });
    }]);
})();
<commit_msg>auto-update pools on apps page<commit_after>/* globals controlplane: true */

/* AppsController
 * Displays top level apps
 */
(function() {
    'use strict';

    controlplane.controller("AppsController", [
        "$scope", "$routeParams", "$location",
        "$notification", "resourcesFactory", "authService",
        "$modalService", "$translate", "$timeout",
        "$cookies", "servicesFactory", "miscUtils",
        "ngTableParams", "$filter", "poolsFactory",
    function($scope, $routeParams, $location,
    $notification, resourcesFactory, authService,
    $modalService, $translate, $timeout,
    $cookies, servicesFactory, utils,
    NgTableParams, $filter, poolsFactory){

        // Ensure logged in
        authService.checkLogin($scope);

        // alias hostname instead of using localhost or IP
        $scope.defaultHostAlias = $location.host();

        // redirect to specific service details
        $scope.routeToService = function(id) {
            resourcesFactory.routeToService(id);
        };

        // redirect to specific pool
        $scope.routeToPool = function(id) {
            resourcesFactory.routeToPool(id);
        };

        $scope.modal_deployWizard = function() {
            // the modal occasionally won't show on page load, so we use a timeout to get around that.
            $timeout(function(){
                $('#addApp').modal('show');

                // don't auto-show this wizard again
                // NOTE: $cookies can only deal with string values
                $cookies.autoRunWizardHasRun = "true";
            });
        };

        $scope.modal_addTemplate = function() {
            $modalService.create({
                templateUrl: "add-template.html",
                model: $scope,
                title: "template_add",
                actions: [
                    {
                        role: "cancel",
                        action: function(){
                            $scope.newHost = {};
                            this.close();
                        }
                    },{
                        role: "ok",
                        label: "template_add",
                        action: function(){
                            if(this.validate()){
                                var data = new FormData();

                                $.each($("#new_template_filename")[0].files, function(key, value){
                                    data.append("tpl", value);
                                });

                                // disable ok button, and store the re-enable function
                                var enableSubmit = this.disableSubmitButton();

                                resourcesFactory.addAppTemplate(data)
                                    .success(function(data){
                                        $notification.create("Added template", data.Detail).success();
                                        refreshTemplates();
                                        this.close();
                                    }.bind(this))
                                    .error(function(data){
                                        this.createNotification("Adding template failed", data.Detail).error();
                                        enableSubmit();
                                    }.bind(this));
                            }
                        }
                    }
                ]
            });
        };

        // aggregate vhosts for a specified service, but
        // only if the service has changed since last request
        $scope.aggregateVHosts = utils.memoize(function(service) {
            var vHosts = [];

            service.model.Endpoints.forEach(endpoint => {
                if(endpoint.VHostList){
                    endpoint.VHostList.forEach(vHost => vHosts.push(vHost));
                }
            });

            vHosts.sort();

            return vHosts;
        }, function(service){
            return service.id + service.model.DatabaseVersion;
        });

        // given a vhost, return a url to it
        $scope.createVHostURL = function(vhost) {
            var port = $location.port() === "" ? "" : ":"+$location.port();
            var host = vhost.Name.indexOf('.') === -1 ? vhost.Name + "." + $scope.defaultHostAlias : vhost.Name;
            return $location.protocol() + "://" + host + port;
        };

        $scope.modal_removeService = function(service) {
            $modalService.create({
                template: $translate.instant("warning_remove_service"),
                model: $scope,
                title: "remove_service",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "remove_service",
                        classes: "btn-danger submit",
                        action: function(){
                            if(this.validate()){
                                this.disableSubmitButton();

                                removeService(service)
                                    .success(() => {
                                        $notification.create("Removed App", service.name).success();
                                        this.close();
                                    })
                                    .error((data, status) => {
                                        $notification.create("Remove App failed", data.Detail).error();
                                        this.close();
                                    });
                            }
                        }
                    }
                ]
            });
        };

        $scope.startService = function(service){
            $scope.modal_startStopService(service, "start");
        };
        $scope.stopService = function(service){
            $scope.modal_startStopService(service, "stop");
        };
        $scope.modal_startStopService = function(service, status) {
            var displayStatus = utils.capitalizeFirst(status);

            $modalService.create({
                template: $translate.instant("confirm_"+ status +"_app"),
                model: $scope,
                title: displayStatus +" Services",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: displayStatus +" Services",
                        action: function(){
                            service[status]();
                            this.close();
                        }
                    }
                ]
            });
        };

        $scope.clickRunningApp = function(service, status) {
            utils.setServiceState($scope, service, status, $modalService, $translate);
        };

        $scope.modal_deleteTemplate = function(templateID){
            $modalService.create({
                template: $translate.instant("template_remove_confirm") + "<strong>"+ templateID +"</strong>",
                model: $scope,
                title: "template_remove",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "template_remove",
                        classes: "btn-danger",
                        action: function(){
                            deleteTemplate(templateID)
                                .success(() => {
                                    $notification.create("Removed Template", templateID).success();
                                    this.close();
                                })
                                .error((data, status) => {
                                    $notification.create("Remove Template failed", data.Detail).error();
                                });
                        }
                    }
                ]
            });
        };

        /*
         * PRIVATE FUNCTIONS
         */
        function refreshTemplates(){
            resourcesFactory.getAppTemplates().success(function(templates) {
                $scope.templates.data = utils.mapToArr(templates);
            });
        }

        // poll for apps that are being deployed
        $scope.deployingServices = [];
        var lastPollResults = 0;
        function getDeploying(){
            resourcesFactory.getDeployingTemplates().success(function(data) {
                if(data){
                    $scope.deployingServices = data;
                }

                //if we have fewer results than last poll, we need to refresh our table
                //TODO - better checking for deploying apps
                if(lastPollResults > $scope.deployingServices.length){
                    servicesFactory.update();
                }
                lastPollResults = $scope.deployingServices.length;
            });
        }

        function removeService(service) {
            return resourcesFactory.removeService(service.id)
                .success(function(){
                    // NOTE: this is here because services are
                    // incrementally updated, which makes it impossible
                    // to determine if a service has been removed
                    // TODO - once the backend notifies on deleted
                    // services, this should be removed
                    // FIXME - should not modify servicesFactory's
                    // objects!
                    for(var i = 0; i < $scope.apps.length; i++){
                        // find the removed service and remove it
                        if($scope.apps[i].id === service.id){
                            $scope.apps.splice(i, 1);
                            return;
                        }
                    }
                });
        }

        function deleteTemplate(templateID){
            return resourcesFactory.removeAppTemplate(templateID)
                .success(refreshTemplates);
        }

        // init stuff for this controller
        function init(){
            if(utils.needsHostAlias($location.host())){
                resourcesFactory.getHostAlias().success(function(data) {
                    $scope.defaultHostAlias = data.hostalias;
                });
            }

            // configure tables
            // TODO - move table config to view/directive
            $scope.breadcrumbs = [
                { label: 'breadcrumb_deployed', itemClass: 'active' }
            ];

            $scope.servicesTable = {
                sorting: {
                    name: "asc"
                },
                getData: function(data, params) {
                    // use built-in angular filter
                    var orderedData = params.sorting() ?
                        $filter('orderBy')(data, params.orderBy()) :
                        data;

                    if(!orderedData){
                        return;
                    }

                    // mark any deploying services so they can be treated differently
                    orderedData.forEach(function(service){
                        service.deploying = false;
                        $scope.deployingServices.forEach(function(deploying){
                            if(service.model.DeploymentID === deploying.DeploymentID){
                                service.deploying = true;
                            }
                        });
                    });

                    return orderedData;
                },
                watchExpression: function(){
                    // TODO - check $scope.deployingServices as well
                    return servicesFactory.lastUpdate;
                }
            };

            $scope.templates = { data: [] };
            // table config
            $scope.templatesTable = {
                sorting: {
                    Name: "asc"
                }
            };

            // Get a list of templates
            refreshTemplates();

            // check for deploying apps
            getDeploying();

            // start polling for apps
            servicesFactory.activate();
            servicesFactory.update().then(function(){
                // locally bind top level service list
                $scope.apps = servicesFactory.serviceTree;

                // if only isvcs are deployed, and this is the first time
                // running deploy wizard, show the deploy apps modal
                if(!$cookies.autoRunWizardHasRun && $scope.apps.length === 1){
                    $scope.modal_deployWizard();
                }
            });

            // deploy wizard needs updated pools
            poolsFactory.activate();

            //register polls
            resourcesFactory.registerPoll("deployingApps", getDeploying, 3000);
            resourcesFactory.registerPoll("templates", refreshTemplates, 5000);

            //unregister polls on destroy
            $scope.$on("$destroy", function(){
                resourcesFactory.unregisterAllPolls();
            });
        }

        // kick this controller off
        init();

        $scope.$on("$destroy", function(){
            resourcesFactory.unregisterAllPolls();
            servicesFactory.deactivate();
            poolsFactory.deactivate();
        });
    }]);
})();
<|endoftext|>


<commit_before>var path = require('path')
var express = require('express')
var webpack = require('webpack')
var config = require('../config')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = process.env.NODE_ENV === 'testing'
  ? require('./webpack.prod.conf')
  : require('./webpack.dev.conf')

// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})

var hotMiddleware = require('webpack-hot-middleware')(compiler)
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(config.build.assetsPublicPath, config.build.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }
  console.log('Listening at http://localhost:' + port + '\n')
})
<commit_msg>Sort prod and env configs out.<commit_after>var path = require('path')
var express = require('express')
var webpack = require('webpack')
var config = require('../config')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = process.env.NODE_ENV === 'testing'
  ? require('./webpack.dev.conf')
  : require('./webpack.prod.conf')

// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})

var hotMiddleware = require('webpack-hot-middleware')(compiler)
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(config.build.assetsPublicPath, config.build.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }
  console.log('Listening at http://localhost:' + port + '\n')
})
<|endoftext|>


<commit_before>
var path = require('path');
var fs = require('fs');
var crypto = require('crypto');

var externalRx = /^(\s*[a-z0-9\-]:)\/\//i;
var absRx = /^\s*\//;
var queryAndHashRx = /[\?\#].*$/;
var slashRx = /\\/g;

var textFiles = ['.css', '.js', '.json', '.tmpl', '.txt', '.svg', '.html'];

var typeByExt = {
  '.js': 'script',
  '.css': 'style',
  '.tmpl': 'template',
  '.html': 'html',
  '.json': 'json',
  '.xml': 'xml',
  '.svg': 'svg',
  '.bmp': 'image',
  '.gif': 'image',
  '.png': 'image',
  '.jpg': 'image',
  '.jpeg': 'image',
  '.tiff': 'image'
};

var typeNotFoundHandler = {
  '.js': '/* Javascript file {filename} not found */',
  '.css': '/* CSS file {filename} not found */'
};

function getFileContentOnFailure(filename){
  return (typeNotFoundHandler[path.extname(filename)] || '').replace(/\{filename\}/, filename);
}

function unixpath(filename){
  return path.normalize(filename).replace(slashRx, '/');
}

function abspath(baseURI, filename){
  return unixpath(path.resolve(baseURI, filename.replace(queryAndHashRx, '')));
}

function isExternal(uri){
  return externalRx.test(uri);
}

/**
 * @class File
 */

function File(manager, cfg){
  this.manager = manager;
  this.linkTo = [];
  this.linkBack = [];

  for (var key in cfg)
    this[key] = cfg[key];

  if (!this.type)
    this.type = typeByExt[this.ext] || 'unknown';
};

File.prototype = {
  resolve: function(filename){
    if (isExternal(filename))
      return filename;

    // remove everything after ? (query string) or # (hash)
    filename = filename.replace(queryAndHashRx, '');

    var rel = filename.replace(absRx, '');
    var result;

    if (rel == filename)
      result = path.resolve(this.baseURI, filename);
    else
      result = path.resolve(this.manager.baseURI, rel);

    return unixpath(result);
  },

  // input filename
  get basename(){
    return this.filename
      ? path.basename(this.filename)
      : '';
  },
  get name(){
    return this.filename
      ? path.basename(this.filename, path.extname(this.filename))
      : '';
  },
  get ext(){
    return this.filename
      ? path.extname(this.filename)
      : '';
  },
  get relpath(){
    return this.filename
      ? unixpath(path.relative(this.manager.baseURI, this.filename))
      : '[inline ' + this.type + ']';
  },

  // input baseURI
  get baseURI(){
    return unixpath(this.filename ? path.dirname(this.filename) + '/' : this.baseURI_ || '');
  },
  set baseURI(uri){
    if (!this.filename)
      this.baseURI_ = unixpath(path.resolve(this.manager.baseURI, uri) + '/');
  },

  // output filename
  get outputFilename(){
    return this.outputFilename_;
  },
  set outputFilename(filename){
    this.outputFilename_ = unixpath(filename);
  },
  get relOutputFilename(){
    return this.outputFilename || '[no output filename]';
  },

  // links
  link: function(file){
    this.linkTo.add(file);
    file.linkBack.add(this);
  },
  linkFrom: function(file){
    file.link(this);
  },
  unlink: function(file){
    this.linkTo.remove(file);
    file.linkBack.remove(this);
  },

  hasLinkTo: function(file){
    return this.linkTo.indexOf(file) != -1;
  },
  hasLinkFrom: function(file){
    return file.hasLinkTo(this);
  },
  hasLinkType: function(type){
    return this.linkBack.some(function(file){
      return file.type == type;
    });
  },

  // misc
  get digest(){
    if (!this.digest_)
    {
      var hash = crypto.createHash('md5');
      hash.update(this.outputContent || this.content);
      this.digest_ = hash.digest('base64')
        // remove trailing == which always appear on md5 digest, save 2 bytes
        .replace(/=+$/, '')
        // make digest web safe
        .replace(/\//g, '_')
        .replace(/\+/g, '-');
    }

    return this.digest_;
  },
  get encoding(){
    return this.type == 'image' /*|| textFiles.indexOf(this.ext) == -1*/ ? 'binary' : 'utf-8';
  }
};

/**
 * @class FileManager
 */

var FileManager = function(baseURI, console){
  this.baseURI = baseURI;
  this.console = console;

  this.map = {};
  this.queue = [];
}

FileManager.prototype = {
  add: function(data){
    var file;

    if (!data.filename)
    {
      file = new File(this, data);
    }
    else
    {
      // ignore references for external resources
      if (isExternal(data.filename))
      {
        // external resource
        this.console.log('[i] External resource `' + data.filename + '` ignored');
        return;
      }

      var filename = abspath(this.baseURI, data.filename);
                     // remove everything after ? (query string) or # (hash)
                     // and normalize

      if (this.map[filename]) // ignore duplicates
      {
        this.console.log('[ ] File `' + unixpath(path.relative(this.baseURI, filename)) + '` already in this.queue');
        return this.map[filename];
      }

      // create file
      data.filename = filename;
      file = new File(this, data);

      // read content
      if (fs.existsSync(filename))
      {
        if (fs.statSync(filename).isFile())
        {
          this.console.log('[+] ' + file.relpath + ' (' + file.type + ')');
          file.content = fs.readFileSync(filename, file.encoding);
        }
        else
        {
          file.warn = '`' + file.relpath + '` is not a file';
        }
      }
      else
      {
        file.warn = 'File `' + file.relpath + '` not found';
      }

      if (file.warn)
      {
        this.console.log('[WARN] ' + file.warn);
        file.content = getFileContentOnFailure(filename);
      }

      this.map[filename] = file;
    }

    this.queue.add(file);

    return file;
  },

  get: function(filename){
    var fileId = abspath(this.baseURI, filename);
    console.log(fileId);

    return this.map[fileId];
  },

  remove: function(filenameOrFile){
    var fileId;
    var file;

    if (filenameOrFile instanceof File)
    {
      file = filenameOrFile;
      fileId = file.filename;
    }
    else
    {
      fileId = abspath(this.baseURI, filenameOrFile);
      file = this.map[fileId];

      if (!file)
      {
        fconsole.log('[WARN] File `' + filenameOrFile + '` not found in map');
        return;
      }
    }

    // remove links
    for (var i = file.linkTo.length, linkTo; linkTo = file.linkTo[i]; i--)
      file.unlink(linkTo);

    for (var i = file.linkBack.length, linkBack; linkBack = file.linkBack[i]; i--)
      linkBack.unlink(file);

    // remove from queue
    this.queue.remove(file);

    // remove from map
    if (fileId)
      delete this.map[fileId];
  },

  mkdir: function(dirpath){
    if (!fs.existsSync(dirpath))
    {
      this.console.log('Create folder ' + dirpath);
      fs.mkdirSync(dirpath);
    }
  }
};

//
// export
//

module.exports = FileManager;
<commit_msg>clean up code, add comments<commit_after>
var path = require('path');
var fs = require('fs');
var crypto = require('crypto');

var externalRx = /^(\s*[a-z0-9\-]:)\/\//i;
var absRx = /^\s*\//;
var queryAndHashRx = /[\?\#].*$/;
var slashRx = /\\/g;

var textFiles = ['.css', '.js', '.json', '.tmpl', '.txt', '.svg', '.html'];

var typeByExt = {
  '.js': 'script',
  '.css': 'style',
  '.tmpl': 'template',
  '.html': 'html',
  '.json': 'json',
  '.xml': 'xml',
  '.svg': 'svg',
  '.bmp': 'image',
  '.gif': 'image',
  '.png': 'image',
  '.jpg': 'image',
  '.jpeg': 'image',
  '.tiff': 'image'
};

var typeNotFoundHandler = {
  '.js': '/* Javascript file {filename} not found */',
  '.css': '/* CSS file {filename} not found */'
};

function getFileContentOnFailure(filename){
  return (typeNotFoundHandler[path.extname(filename)] || '').replace(/\{filename\}/, filename);
}

function unixpath(filename){
  return path.normalize(filename).replace(slashRx, '/');
}

function abspath(baseURI, filename){
  return unixpath(path.resolve(baseURI, filename.replace(queryAndHashRx, '')));
}

function isExternal(uri){
  return externalRx.test(uri);
}

/**
 * @class File
 */

function File(manager, cfg){
  this.manager = manager;
  this.linkTo = [];
  this.linkBack = [];

  for (var key in cfg)
    this[key] = cfg[key];

  if (!this.type)
    this.type = typeByExt[this.ext] || 'unknown';
};

File.prototype = {
  resolve: function(filename){
    if (isExternal(filename))
      return filename;

    // remove everything after ? (query string) or # (hash)
    filename = filename.replace(queryAndHashRx, '');

    var rel = filename.replace(absRx, '');
    var result;

    if (rel == filename)
      result = path.resolve(this.baseURI, filename);
    else
      result = path.resolve(this.manager.baseURI, rel);

    return unixpath(result);
  },

  // input filename
  get basename(){
    return this.filename
      ? path.basename(this.filename)
      : '';
  },
  get name(){
    return this.filename
      ? path.basename(this.filename, path.extname(this.filename))
      : '';
  },
  get ext(){
    return this.filename
      ? path.extname(this.filename)
      : '';
  },
  get relpath(){
    return this.filename
      ? unixpath(path.relative(this.manager.baseURI, this.filename))
      : '[inline ' + this.type + ']';
  },

  // input baseURI
  get baseURI(){
    return unixpath(this.filename ? path.dirname(this.filename) + '/' : this.baseURI_ || '');
  },
  set baseURI(uri){
    if (!this.filename)
      this.baseURI_ = unixpath(path.resolve(this.manager.baseURI, uri) + '/');
  },

  // output filename
  get outputFilename(){
    return this.outputFilename_;
  },
  set outputFilename(filename){
    this.outputFilename_ = unixpath(filename);
  },
  get relOutputFilename(){
    return this.outputFilename || '[no output filename]';
  },

  // links
  link: function(file){
    this.linkTo.add(file);
    file.linkBack.add(this);
  },
  linkFrom: function(file){
    file.link(this);
  },
  unlink: function(file){
    this.linkTo.remove(file);
    file.linkBack.remove(this);
  },

  hasLinkTo: function(file){
    return this.linkTo.indexOf(file) != -1;
  },
  hasLinkFrom: function(file){
    return file.hasLinkTo(this);
  },
  hasLinkType: function(type){
    return this.linkBack.some(function(file){
      return file.type == type;
    });
  },

  // misc
  get digest(){
    if (!this.digest_)
    {
      var hash = crypto.createHash('md5');
      hash.update(this.outputContent || this.content);
      this.digest_ = hash.digest('base64')
        // remove trailing == which always appear on md5 digest, save 2 bytes
        .replace(/=+$/, '')
        // make digest web safe
        .replace(/\//g, '_')
        .replace(/\+/g, '-');
    }

    return this.digest_;
  },
  get encoding(){
    return this.type == 'image' /*|| textFiles.indexOf(this.ext) == -1*/ ? 'binary' : 'utf-8';
  }
};

/**
 * @class FileManager
 */

var FileManager = function(baseURI, console){
  this.baseURI = baseURI;
  this.console = console;

  this.map = {};
  this.queue = [];
}

FileManager.prototype = {
 /**
  * Get reference for File by filename.
  * @param {string} filename Path to file.
  * @returns {File} Returns file if exists.
  */
  get: function(filename){
    return this.map[abspath(this.baseURI, filename)];
  },

 /**
  * Create new file or return existing. It can returns undefined if filename is external reference.
  * @params {object} data Config object to create new file.
  * @return {File|undefined}
  *
  * TODO: extend file with, if it already exists
  * TODO: reate file with uri, if it's external?
  */
  add: function(data){
    var file;

    if (!data.filename)
    {
      file = new File(this, data);
    }
    else
    {
      // ignore references for external resources
      if (isExternal(data.filename))
      {
        // external resource
        this.console.log('[i] External resource `' + data.filename + '` ignored');
        return;
      }

      var filename = abspath(this.baseURI, data.filename);
                     // remove everything after ? (query string) or # (hash)
                     // and normalize

      if (this.map[filename]) // ignore duplicates
      {
        this.console.log('[ ] File `' + unixpath(path.relative(this.baseURI, filename)) + '` already in this.queue');
        return this.map[filename];
      }

      // create file
      data.filename = filename;
      file = new File(this, data);

      // read content
      if (fs.existsSync(filename))
      {
        if (fs.statSync(filename).isFile())
        {
          this.console.log('[+] ' + file.relpath + ' (' + file.type + ')');
          file.content = fs.readFileSync(filename, file.encoding);
        }
        else
        {
          file.warn = '`' + file.relpath + '` is not a file';
        }
      }
      else
      {
        file.warn = 'File `' + file.relpath + '` not found';
      }

      if (file.warn)
      {
        this.console.log('[WARN] ' + file.warn);
        file.content = getFileContentOnFailure(filename);
      }

      this.map[filename] = file;
    }

    this.queue.add(file);

    return file;
  },

 /**
  * Remove a file from manager and break all links between files.
  * @param {File|string} fileRef File name or File instance to be removed.
  */
  remove: function(fileRef){
    var filename;
    var file;

    if (fileRef instanceof File)
    {
      file = fileRef;
      filename = file.filename;
    }
    else
    {
      filename = abspath(this.baseURI, fileRef);
      file = this.map[filename];

      if (!file)
      {
        fconsole.log('[WARN] File `' + fileRef + '` not found in map');
        return;
      }
    }

    // remove links
    for (var i = file.linkTo.length, linkTo; linkTo = file.linkTo[i]; i--)
      file.unlink(linkTo);

    for (var i = file.linkBack.length, linkBack; linkBack = file.linkBack[i]; i--)
      linkBack.unlink(file);

    // remove from queue
    this.queue.remove(file);

    // remove from map
    if (filename)
      delete this.map[filename];
  },

  mkdir: function(dirpath){
    if (!fs.existsSync(dirpath))
    {
      this.console.log('Create folder ' + dirpath);
      fs.mkdirSync(dirpath);
    }
  }
};

//
// export
//

module.exports = FileManager;
<|endoftext|>


<commit_before>var shell = require("shelljs");

module.exports = function (commandOrOptions, errorMessage) {

    var command = commandOrOptions.command || commandOrOptions,
        options = {};

    if (commandOrOptions.silent) {
        options.silent = true;
    }

    errorMessage = errorMessage || "Error executing command: " + command;

    var result = shell.exec(command, options);
    if (result.code !== 0) {
        throw new Error(errorMessage);
    }

    return result.output;
};
<commit_msg>reworded exec error message<commit_after>var shell = require("shelljs");

module.exports = function (commandOrOptions, errorMessage) {

    var command = commandOrOptions.command || commandOrOptions,
        options = {};

    if (commandOrOptions.silent) {
        options.silent = true;
    }

    errorMessage = errorMessage || "Cannot execute command: " + command;

    var result = shell.exec(command, options);
    if (result.code !== 0) {
        throw new Error(errorMessage);
    }

    return result.output;
};
<|endoftext|>


<commit_before>'use strict';

(function() {
  // Login Controller Spec
  describe('MEAN controllers', function() {
    describe('LoginCtrl', function() {
      beforeEach(function() {
        jasmine.addMatchers({
          toEqualData: function() {
            return {
              compare: function(actual, expected) {
                return {
                  pass: angular.equals(actual, expected)
                };
              }
            };
          }
        });
      });

      beforeEach(function() {
        module('mean');
        module('mean.system');
        module('mean.users');
      });

      var LoginCtrl,
        scope,
        $rootScope,
        $httpBackend,
        $location;

      beforeEach(inject(function($controller, _$rootScope_, _$location_, _$httpBackend_) {

        scope = _$rootScope_.$new();
        $rootScope = _$rootScope_;

        LoginCtrl = $controller('LoginCtrl', {
          $scope: scope,
          $rootScope: _$rootScope_
        });

        $httpBackend = _$httpBackend_;

        $location = _$location_;

      }));

      afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
      });

      it('should login with a correct user and password', function() {

        spyOn($rootScope, '$emit');
        // test expected GET request
        $httpBackend.when('POST', '/login').respond(200, {
          user: 'Fred'
        });
        scope.login();
        $httpBackend.flush();
        // test scope value
        expect($rootScope.user).toEqual('Fred');
        expect($rootScope.$emit).toHaveBeenCalledWith('loggedin');
        expect($location.url()).toEqual('/');
      });

      it('should fail to log in ', function() {
        $httpBackend.expectPOST('/login').respond(400, 'Authentication failed');
        scope.login();
        $httpBackend.flush();
        // test scope value
        expect(scope.loginerror).toEqual('Authentication failed.');

      });
    });

    describe('RegisterCtrl', function() {
      beforeEach(function() {
        jasmine.addMatchers({
          toEqualData: function() {
            return {
              compare: function(actual, expected) {
                return {
                  pass: angular.equals(actual, expected)
                };
              }
            };
          }
        });
      });

      beforeEach(function() {
        module('mean');
        module('mean.system');
        module('mean.users');
      });

      var RegisterCtrl,
        scope,
        $rootScope,
        $httpBackend,
        $location;

      beforeEach(inject(function($controller, _$rootScope_, _$location_, _$httpBackend_) {

        scope = _$rootScope_.$new();
        $rootScope = _$rootScope_;

        RegisterCtrl = $controller('RegisterCtrl', {
          $scope: scope,
          $rootScope: _$rootScope_
        });

        $httpBackend = _$httpBackend_;

        $location = _$location_;

      }));

      afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
      });

      it('should register with correct data', function() {

        spyOn($rootScope, '$emit');
        // test expected GET request
        scope.user.name = 'Fred';
        $httpBackend.when('POST', '/register').respond(200, 'Fred');
        scope.register();
        $httpBackend.flush();
        // test scope value
        expect($rootScope.user.name).toBe('Fred');
        expect(scope.registerError).toEqual(0);
        expect($rootScope.$emit).toHaveBeenCalledWith('loggedin');
        expect($location.url()).toBe('/');
      });

      it('should fail to register with duplicate Username', function() {
        $httpBackend.when('POST', '/register').respond(400, 'Username already taken');
        scope.register();
        $httpBackend.flush();
        // test scope value
        expect(scope.usernameError).toBe('Username already taken');
        expect(scope.registerError).toBe(null);
      });

      it('should fail to register with non-matching passwords', function() {
        $httpBackend.when('POST', '/register').respond(400, 'Password mismatch');
        scope.register();
        $httpBackend.flush();
        // test scope value
        expect(scope.usernameError).toBe(null);
        expect(scope.registerError).toBe('Password mismatch');
      });
    });
  });

}());
<commit_msg>adding karma unit tests for ForgetPasswordCtrl.<commit_after>'use strict';

(function() {
  // Login Controller Spec
  describe('MEAN controllers', function() {
    describe('LoginCtrl', function() {
      beforeEach(function() {
        jasmine.addMatchers({
          toEqualData: function() {
            return {
              compare: function(actual, expected) {
                return {
                  pass: angular.equals(actual, expected)
                };
              }
            };
          }
        });
      });

      beforeEach(function() {
        module('mean');
        module('mean.system');
        module('mean.users');
      });

      var LoginCtrl,
        scope,
        $rootScope,
        $httpBackend,
        $location;

      beforeEach(inject(function($controller, _$rootScope_, _$location_, _$httpBackend_) {

        scope = _$rootScope_.$new();
        $rootScope = _$rootScope_;

        LoginCtrl = $controller('LoginCtrl', {
          $scope: scope,
          $rootScope: _$rootScope_
        });

        $httpBackend = _$httpBackend_;

        $location = _$location_;

      }));

      afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
      });

      it('should login with a correct user and password', function() {

        spyOn($rootScope, '$emit');
        // test expected GET request
        $httpBackend.when('POST', '/login').respond(200, {
          user: 'Fred'
        });
        scope.login();
        $httpBackend.flush();
        // test scope value
        expect($rootScope.user).toEqual('Fred');
        expect($rootScope.$emit).toHaveBeenCalledWith('loggedin');
        expect($location.url()).toEqual('/');
      });

      it('should fail to log in ', function() {
        $httpBackend.expectPOST('/login').respond(400, 'Authentication failed');
        scope.login();
        $httpBackend.flush();
        // test scope value
        expect(scope.loginerror).toEqual('Authentication failed.');

      });
    });

    describe('RegisterCtrl', function() {
      beforeEach(function() {
        jasmine.addMatchers({
          toEqualData: function() {
            return {
              compare: function(actual, expected) {
                return {
                  pass: angular.equals(actual, expected)
                };
              }
            };
          }
        });
      });

      beforeEach(function() {
        module('mean');
        module('mean.system');
        module('mean.users');
      });

      var RegisterCtrl,
        scope,
        $rootScope,
        $httpBackend,
        $location;

      beforeEach(inject(function($controller, _$rootScope_, _$location_, _$httpBackend_) {

        scope = _$rootScope_.$new();
        $rootScope = _$rootScope_;

        RegisterCtrl = $controller('RegisterCtrl', {
          $scope: scope,
          $rootScope: _$rootScope_
        });

        $httpBackend = _$httpBackend_;

        $location = _$location_;

      }));

      afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
      });

      it('should register with correct data', function() {

        spyOn($rootScope, '$emit');
        // test expected GET request
        scope.user.name = 'Fred';
        $httpBackend.when('POST', '/register').respond(200, 'Fred');
        scope.register();
        $httpBackend.flush();
        // test scope value
        expect($rootScope.user.name).toBe('Fred');
        expect(scope.registerError).toEqual(0);
        expect($rootScope.$emit).toHaveBeenCalledWith('loggedin');
        expect($location.url()).toBe('/');
      });

      it('should fail to register with duplicate Username', function() {
        $httpBackend.when('POST', '/register').respond(400, 'Username already taken');
        scope.register();
        $httpBackend.flush();
        // test scope value
        expect(scope.usernameError).toBe('Username already taken');
        expect(scope.registerError).toBe(null);
      });

      it('should fail to register with non-matching passwords', function() {
        $httpBackend.when('POST', '/register').respond(400, 'Password mismatch');
        scope.register();
        $httpBackend.flush();
        // test scope value
        expect(scope.usernameError).toBe(null);
        expect(scope.registerError).toBe('Password mismatch');
      });
    });

    describe('ForgotPasswordCtrl', function() {
      beforeEach(function() {
        jasmine.addMatchers({
          toEqualData: function() {
            return {
              compare: function(actual, expected) {
                return {
                  pass: angular.equals(actual, expected)
                };
              }
            };
          }
        });
      });

      beforeEach(function() {
        module('mean');
        module('mean.system');
        module('mean.users');
      });

      var ForgotPasswordCtrl,
          scope,
          $rootScope,
          $httpBackend,
          $locaion;

      beforeEach(inject(function($controller, _$rootScope_, _$httpBackend_, _$location_ ) {

        scope = _$rootScope_.$new();
        $rootScope = _$rootScope_;

        ForgotPasswordCtrl = $controller('ForgotPasswordCtrl', {
          $scope: scope,
          $rootScope: _$rootScope_
        });

        $httpBackend = _$httpBackend_;
        $locaion   = _$location_;

      }));

      afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
      });

      it('should display success response on success', function() {
        scope.user.email = "test@test.com";
        $httpBackend.when('POST', '/forgot-password').respond(200,'Mail successfully sent');
        scope.forgotpassword();
        $httpBackend.flush();

        expect(scope.response).toEqual('Mail successfully sent');

      });
      it('should display error response on failure', function() {
        scope.user.email = "test@test.com";
        $httpBackend.when('POST', '/forgot-password').respond(400,'User does not exist');
        scope.forgotpassword();
        $httpBackend.flush();

        expect(scope.response).toEqual('User does not exist');

      });

    });
  });

}());
<|endoftext|>


<commit_before>import WebMiddle from "webmiddle";
import superagent from "superagent";
import WebSocket from "ws";
import uuid from "uuid";
import test from "ava";
import Server from "../src";

const PORT = 3000;

function requestExpress(method, path, data = {}) {
  return new Promise((resolve, reject) => {
    superagent
      [method.toLowerCase()]("http://localhost:" + PORT + path)
      .send(data)
      .end((err, res) => {
        if (err) reject(err);
        else resolve(res.body);
      });
  });
}

let wsPromise;
function getWebsocketConnection() {
  if (!wsPromise) {
    wsPromise = new Promise((resolve, reject) => {
      try {
        const ws = new WebSocket(`ws://localhost:${PORT}/`);
        ws.on("open", () => resolve(ws));
        ws.on("error", reject);
      } catch (err) {
        reject(err);
      }
    });
  }
  return wsPromise;
}

function requestWebsocket(path, body = {}, onProgress) {
  return getWebsocketConnection().then(ws => {
    return new Promise((resolve, reject) => {
      try {
        const requestId = uuid.v4();

        ws.on("message", rawMessage => {
          //console.log('received from server: %s', rawMessage);
          const message = JSON.parse(rawMessage);
          if (message.requestId !== requestId) return;

          if (message.type === "progress") {
            if (onProgress) onProgress(message);
          }

          if (message.type === "response") {
            if (message.status === "success") {
              resolve(message.body);
            } else if (message.status === "error") {
              reject(message.body);
            }
          }
        });

        ws.send(JSON.stringify({ type: "request", requestId, path, body }));
      } catch (err) {
        console.error(err instanceof Error ? err.stack : err);
        reject(err instanceof Error ? err.stack : err);
      }
    });
  });
}

const webmiddle = new WebMiddle({
  services: {
    math: {
      sum: ({ a, b }) => ({
        name: "result",
        contentType: "text/plain",
        // without Number() a + b would be a string concatenation in GET requests!
        content: String(Number(a) + Number(b))
      }),

      multiply: ({ a, b }) => ({
        name: "result",
        contentType: "text/plain",
        content: String(Number(a) * Number(b))
      }),

      divide: ({ a, b }) => ({
        name: "result",
        contentType: "text/plain",
        content: String(Number(a) / Number(b))
      })
    },
    returnOption: ({ optionName }, context) => context.options[optionName]
  },
  settings: {
    foo: {
      some: "bar",
      other: 100
    }
  }
});

const server = new Server(webmiddle, { port: PORT });
server.start();

test("Execute service via GET", async t => {
  const resource = await requestExpress("GET", "/services/math/sum?a=5&b=10");
  t.deepEqual(resource, {
    name: "result",
    contentType: "text/plain",
    content: "15"
  });
});

test("Execute service via POST", async t => {
  const resource = await requestExpress("POST", "/services/math/multiply", {
    props: {
      a: 20,
      b: 5
    }
  });
  t.deepEqual(resource, {
    name: "result",
    contentType: "text/plain",
    content: "100"
  });
});

test("Execute service via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/services/math/multiply", {
    props: {
      a: 20,
      b: 5
    }
  });
  t.deepEqual(resource, {
    name: "result",
    contentType: "text/plain",
    content: "100"
  });
});

test("Must throw when executing a non existing service via GET", async t => {
  await t.throws(
    requestExpress("GET", "/services/wrongUndefinedService?a=5&b=10")
  );
});

test("Must throw when executing a non existing service via POST", async t => {
  await t.throws(requestExpress("POST", "/services/wrongUndefinedService"));
});

test("Must throw when executing a non existing service via WEBSOCKET", async t => {
  await t.throws(requestWebsocket("/services/wrongUndefinedService"));
});

test("Pass context options to service via POST", async t => {
  const resource = await requestExpress("POST", "/services/returnOption", {
    props: {
      optionName: "custom"
    },
    options: {
      custom: 5
    }
  });
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 5);
});

test("Pass context options to service via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/services/returnOption", {
    props: {
      optionName: "custom"
    },
    options: {
      custom: 5
    }
  });
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 5);
});

test("Get progress when executing a service via WEBSOCKET", async t => {
  const progressData = [];
  function handleProgress(message) {
    progressData.push(message.body);
  }

  await requestWebsocket(
    "/services/math/multiply",
    {
      props: {
        a: 20,
        b: 5
      }
    },
    handleProgress
  );

  t.is(progressData[0].path, "0");
  t.true(
    typeof progressData[0].info === "object" && progressData[0].info !== null
  );
});

test("Read setting via GET", async t => {
  const resource = await requestExpress("GET", "/settings/foo/other");
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 100);
});

test("Read setting via POST", async t => {
  const resource = await requestExpress("POST", "/settings/foo/other");
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 100);
});

test("Read setting via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/settings/foo/other");
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 100);
});

test("Get service paths", async t => {
  const resource = await requestExpress("GET", "/services/");
  t.deepEqual(resource, {
    name: "services",
    contentType: "application/json",
    content: ["math.sum", "math.multiply", "math.divide", "returnOption"]
  });
});

test("Get service paths via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/services/");
  t.deepEqual(resource, {
    name: "services",
    contentType: "application/json",
    content: ["math.sum", "math.multiply", "math.divide", "returnOption"]
  });
});

test("Get setting paths", async t => {
  const resource = await requestExpress("GET", "/settings/");
  t.deepEqual(resource, {
    name: "settings",
    contentType: "application/json",
    content: ["foo.some", "foo.other"]
  });
});

test("Get setting paths via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/settings/");
  t.deepEqual(resource, {
    name: "settings",
    contentType: "application/json",
    content: ["foo.some", "foo.other"]
  });
});
<commit_msg>webmiddle-server: fix tests<commit_after>import WebMiddle from "webmiddle";
import superagent from "superagent";
import WebSocket from "ws";
import uuid from "uuid";
import test from "ava";
import Server from "../src";

const PORT = 3000;

function requestExpress(method, path, data = {}) {
  return new Promise((resolve, reject) => {
    superagent
      [method.toLowerCase()]("http://localhost:" + PORT + path)
      .send(data)
      .end((err, res) => {
        if (err) reject(err);
        else resolve(res.body);
      });
  });
}

let wsPromise;
function getWebsocketConnection() {
  if (!wsPromise) {
    wsPromise = new Promise((resolve, reject) => {
      try {
        const ws = new WebSocket(`ws://localhost:${PORT}/`);
        ws.on("open", () => resolve(ws));
        ws.on("error", reject);
      } catch (err) {
        reject(err);
      }
    });
  }
  return wsPromise;
}

function requestWebsocket(path, body = {}, onProgress) {
  return getWebsocketConnection().then(ws => {
    return new Promise((resolve, reject) => {
      try {
        const requestId = uuid.v4();

        ws.on("message", rawMessage => {
          //console.log('received from server: %s', rawMessage);
          const message = JSON.parse(rawMessage);
          if (message.requestId !== requestId) return;

          if (message.type === "progress") {
            if (onProgress) onProgress(message);
          }

          if (message.type === "response") {
            if (message.status === "success") {
              resolve(message.body);
            } else if (message.status === "error") {
              reject(message.body);
            }
          }
        });

        ws.send(JSON.stringify({ type: "request", requestId, path, body }));
      } catch (err) {
        console.error(err instanceof Error ? err.stack : err);
        reject(err instanceof Error ? err.stack : err);
      }
    });
  });
}

const webmiddle = new WebMiddle({
  services: {
    math: {
      sum: ({ a, b }) => ({
        name: "result",
        contentType: "text/plain",
        // without Number() a + b would be a string concatenation in GET requests!
        content: String(Number(a) + Number(b))
      }),

      multiply: ({ a, b }) => ({
        name: "result",
        contentType: "text/plain",
        content: String(Number(a) * Number(b))
      }),

      divide: ({ a, b }) => ({
        name: "result",
        contentType: "text/plain",
        content: String(Number(a) / Number(b))
      })
    },
    returnOption: ({ optionName }, context) => context.options[optionName]
  },
  settings: {
    foo: {
      some: "bar",
      other: 100
    }
  }
});

const server = new Server(webmiddle, { port: PORT });
server.start();

test("Execute service via GET", async t => {
  const resource = await requestExpress("GET", "/services/math/sum?a=5&b=10");
  t.deepEqual(resource, {
    name: "result",
    contentType: "text/plain",
    content: "15"
  });
});

test("Execute service via POST", async t => {
  const resource = await requestExpress("POST", "/services/math/multiply", {
    props: {
      a: 20,
      b: 5
    }
  });
  t.deepEqual(resource, {
    name: "result",
    contentType: "text/plain",
    content: "100"
  });
});

test("Execute service via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/services/math/multiply", {
    props: {
      a: 20,
      b: 5
    }
  });
  t.deepEqual(resource, {
    name: "result",
    contentType: "text/plain",
    content: "100"
  });
});

test("Must throw when executing a non existing service via GET", async t => {
  await t.throws(
    requestExpress("GET", "/services/wrongUndefinedService?a=5&b=10")
  );
});

test("Must throw when executing a non existing service via POST", async t => {
  await t.throws(requestExpress("POST", "/services/wrongUndefinedService"));
});

test("Must throw when executing a non existing service via WEBSOCKET", async t => {
  await t.throws(requestWebsocket("/services/wrongUndefinedService"));
});

test("Pass context options to service via POST", async t => {
  const resource = await requestExpress("POST", "/services/returnOption", {
    props: {
      optionName: "custom"
    },
    options: {
      custom: 5
    }
  });
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 5);
});

test("Pass context options to service via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/services/returnOption", {
    props: {
      optionName: "custom"
    },
    options: {
      custom: 5
    }
  });
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 5);
});

test("Get progress when executing a service via WEBSOCKET", async t => {
  const progressData = [];
  function handleProgress(message) {
    progressData.push(message.body);
  }

  await requestWebsocket(
    "/services/math/multiply",
    {
      props: {
        a: 20,
        b: 5
      },
      options: {
        debug: true
      }
    },
    handleProgress
  );

  t.is(progressData[0].path, "0");
  t.true(
    typeof progressData[0].info === "object" && progressData[0].info !== null
  );
});

test("Read setting via GET", async t => {
  const resource = await requestExpress("GET", "/settings/foo/other");
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 100);
});

test("Read setting via POST", async t => {
  const resource = await requestExpress("POST", "/settings/foo/other");
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 100);
});

test("Read setting via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/settings/foo/other");
  t.is(resource.contentType, "x-webmiddle-any");
  t.is(resource.content, 100);
});

test("Get service paths", async t => {
  const resource = await requestExpress("GET", "/services/");
  t.deepEqual(resource, {
    name: "services",
    contentType: "application/json",
    content: ["math.sum", "math.multiply", "math.divide", "returnOption"]
  });
});

test("Get service paths via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/services/");
  t.deepEqual(resource, {
    name: "services",
    contentType: "application/json",
    content: ["math.sum", "math.multiply", "math.divide", "returnOption"]
  });
});

test("Get setting paths", async t => {
  const resource = await requestExpress("GET", "/settings/");
  t.deepEqual(resource, {
    name: "settings",
    contentType: "application/json",
    content: ["foo.some", "foo.other"]
  });
});

test("Get setting paths via WEBSOCKET", async t => {
  const resource = await requestWebsocket("/settings/");
  t.deepEqual(resource, {
    name: "settings",
    contentType: "application/json",
    content: ["foo.some", "foo.other"]
  });
});
<|endoftext|>


<commit_before>define(["dojo/_base/lang", "dojo/_base/declare", "dojo/on", "dojo/_base/connect", "dojo/_base/fx", "dojox/gfx", "dojox/widget/_Invalidating", "./IndicatorBase"],
	function(lang, declare, on, connect, fx, gfx, _Invalidating, IndicatorBase){
	return declare("dojox.dgauges.ScaleIndicatorBase", IndicatorBase, {
		// summary:
		//		The base class for indicators that rely on a scale for their rendering.
		//		Typically, value indicators and range indicators are subclasses of ScaleIndicatorBase.

		// summary:
		//		The scale associated with this indicator.
		scale: null,
		// summary:
		//		The value of the indicator. Default is 0.
		value: 0,

		// interactionArea: String
		//		How to interact with the indicator using mouse or touch interactions.
		//		Can be "indicator", "gauge", "area" or "none". The default value is "gauge".
		//		If set to "indicator", the indicator shape reacts to mouse and touch events.
		//		If set to "gauge", the whole gauge reacts to mouse and touch events.
		//		If set to "area", the whole bounding box of the widget reacts to mouse and touch events.
		//		If "none", interactions are disabled.
		interactionArea: "gauge",

		// interactionMode: String
		//		Can be "mouse" or "touch".
		interactionMode: "mouse",

		// animationDuration: Number
		//		The duration of the value change animation in milliseconds. Default is 0.
		//		The animation occurs on both user interactions and programmatic value changes.
		//		Set this property to 0 to disable animation.
		animationDuration: 0,

		// animationEaser: Object
		//		The easer function of the value change animation. Default is fx._defaultEasing.
		animationEaser: null,

		_indicatorShapeFuncFlag: true,

		_interactionAreaFlag: true,

		_downListeners: null,

		_cursorListeners: null,

		_moveAndUpListeners: null,

		_transitionValue: NaN,

		_preventAnimation: false,

		_animation: null,

		constructor: function(){

			// watches changed happening on the "value" property to call this.valueChanged() function which
			// can be listen by user with connect.connect
			this.watch("value", lang.hitch(this, function(){
				this.valueChanged(this);
			}));
			this.watch("value", lang.hitch(this, this._startAnimation));

			this.watch("interactionArea", lang.hitch(this, function(){
				this._interactionAreaFlag = true;
			}));
			this.watch("interactionMode", lang.hitch(this, function(){
				this._interactionAreaFlag = true;
			}));

			this.watch("indicatorShapeFunc", lang.hitch(this, function(){
				this._indicatorShapeFuncFlag = true;
			}));

			this.addInvalidatingProperties(["scale", "value", "indicatorShapeFunc", "interactionArea", "interactionMode"]);

			this._downListeners = [];
			this._moveAndUpListeners = [];
			this._cursorListeners = [];
		},

		_startAnimation: function(prop, oldValue, newValue){
			// summary:
			//		Internal method.
			// tags:
			//		private
			if(this.animationDuration == 0){
				return;
			}
			if(this._animation && (this._preventAnimation || oldValue == newValue)){
				this._animation.stop();
				return;
			}
			this._animation = new fx.Animation({curve: [oldValue, newValue],
										duration: this.animationDuration,
										easing: this.animationEaser ? this.animationEaser : fx._defaultEasing,
										onAnimate: lang.hitch(this, this._updateAnimation),
										onEnd: lang.hitch(this, this._endAnimation),
										onStop: lang.hitch(this, this._endAnimation)});

			this._animation.play();
		},

		_updateAnimation: function(v){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._transitionValue = v;
			this.invalidateRendering();
		},

		_endAnimation: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._transitionValue = NaN;
			this.invalidateRendering();
		},

		refreshRendering: function(){
			if(this._gfxGroup == null || this.scale == null){
				return;
			}else{
				if(this._indicatorShapeFuncFlag && lang.isFunction(this.indicatorShapeFunc)){
					this._gfxGroup.clear();
					this.indicatorShapeFunc(this._gfxGroup, this);
					this._indicatorShapeFuncFlag = false;
				}

				if(this._interactionAreaFlag){
					this._interactionAreaFlag = this._connectDownListeners();
				}
			}
		},

		valueChanged: function(indicator){
			// summary:
			//		Invoked when the value of the indicator changes.
			//		User can connect an listener on this function:
			// |	connect.connect(theIndicator, "valueChanged", lang.hitch(this, function(){
			// |		//do something
			// |	}));
			on.emit(this, "valueChanged", {
				target: this,
				bubbles: true,
				cancelable: true
			});
		},

		_disconnectDownListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			for(var i = 0; i < this._downListeners.length; i++){
				connect.disconnect(this._downListeners[i]);
			}
			this._downListeners = [];
		},

		_disconnectMoveAndUpListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			for(var i = 0; i < this._moveAndUpListeners.length; i++){
				connect.disconnect(this._moveAndUpListeners[i]);
			}
			this._moveAndUpListeners = [];
		},

		_disconnectListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._disconnectDownListeners();
			this._disconnectMoveAndUpListeners();
			this._disconnectCursorListeners();
		},

		_connectCursorListeners: function(target){
			// summary:
			//		Internal method.
			// tags:
			//		private
			var listener = target.connect("onmouseover", this, function(){
				this.scale._gauge._setCursor("pointer");
			});
			this._cursorListeners.push(listener);
			listener = target.connect("onmouseout", this, function(event){
					this.scale._gauge._setCursor("");
				}
			);
			this._cursorListeners.push(listener);
		},

		_disconnectCursorListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			for(var i = 0; i < this._cursorListeners.length; i++){
				connect.disconnect(this._cursorListeners[i]);
			}
			this._cursorListeners = [];
		},

		_connectDownListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._disconnectDownListeners();
			this._disconnectCursorListeners();
			var listener = null;
			var downEventName;
			if(this.interactionMode == "mouse"){
				downEventName = "onmousedown";
			}else if(this.interactionMode == "touch"){
				downEventName = "ontouchstart";
			}

			if(this.interactionMode == "mouse" || this.interactionMode == "touch"){
				if(this.interactionArea == "indicator"){
					listener = this._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					if (this.interactionMode == "mouse") {
						this._connectCursorListeners(this._gfxGroup);
					}
				}else if(this.interactionArea == "gauge"){
					if(!this.scale || !this.scale._gauge || !this.scale._gauge._gfxGroup){
						return true;
					}
					listener = this.scale._gauge._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					listener = this._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					if (this.interactionMode == "mouse") {
						this._connectCursorListeners(this.scale._gauge._gfxGroup);
					}
				}else if(this.interactionArea == "area"){
					if(!this.scale || !this.scale._gauge || !this.scale._gauge._baseGroup){
						return true;
					}
					listener = this.scale._gauge._baseGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					listener = this._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					if (this.interactionMode == "mouse") {
						this._connectCursorListeners(this.scale._gauge._baseGroup);
					}
				}
			}
			return false;
		},

		_connectMoveAndUpListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			var listener = null;
			var moveEventName;
			var upEventName;
			if(this.interactionMode == "mouse"){
				moveEventName = "onmousemove";
				upEventName = "onmouseup";
			}else if(this.interactionMode == "touch"){
				moveEventName = "ontouchmove";
				upEventName = "ontouchend";
			}
			listener = this.scale._gauge._baseGroup.connect(moveEventName, this, this._onMouseMove);
			this._moveAndUpListeners.push(listener);
			listener = this._gfxGroup.connect(moveEventName, this, this._onMouseMove);
			this._moveAndUpListeners.push(listener);

			listener = this.scale._gauge._baseGroup.connect(upEventName, this, this._onMouseUp);
			this._moveAndUpListeners.push(listener);
			listener = this._gfxGroup.connect(upEventName, this, this._onMouseUp);
			this._moveAndUpListeners.push(listener);
		},

		_onMouseDown: function(event){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._connectMoveAndUpListeners();
			this._startEditing();
		},

		_onMouseMove: function(event){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._preventAnimation = true;
			if(this._animation){
				this._animation.stop();
			}
		},

		_onMouseUp: function(event){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._disconnectMoveAndUpListeners();
			this._preventAnimation = false;
			this._endEditing();
		},

		_startEditing: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			if(!this.scale || !this.scale._gauge){
				return;
			}else{
				this.scale._gauge.onStartEditing({
					indicator: this
				});
			}
		},

		_endEditing: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			if(!this.scale || !this.scale._gauge){
				return;
			}else{
				this.scale._gauge.onEndEditing({
					indicator: this
				});
			}
		}

	});
});
<commit_msg>fix #10<commit_after>define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/window", "dojo/on", "dojo/_base/connect", "dojo/_base/fx", "dojox/gfx", "dojox/widget/_Invalidating", "./IndicatorBase"],
	function(lang, declare, win, on, connect, fx, gfx, _Invalidating, IndicatorBase){
	return declare("dojox.dgauges.ScaleIndicatorBase", IndicatorBase, {
		// summary:
		//		The base class for indicators that rely on a scale for their rendering.
		//		Typically, value indicators and range indicators are subclasses of ScaleIndicatorBase.

		// summary:
		//		The scale associated with this indicator.
		scale: null,
		// summary:
		//		The value of the indicator. Default is 0.
		value: 0,

		// interactionArea: String
		//		How to interact with the indicator using mouse or touch interactions.
		//		Can be "indicator", "gauge", "area" or "none". The default value is "gauge".
		//		If set to "indicator", the indicator shape reacts to mouse and touch events.
		//		If set to "gauge", the whole gauge reacts to mouse and touch events.
		//		If set to "area", the whole bounding box of the widget reacts to mouse and touch events.
		//		If "none", interactions are disabled.
		interactionArea: "gauge",

		// interactionMode: String
		//		Can be "mouse" or "touch".
		interactionMode: "mouse",

		// animationDuration: Number
		//		The duration of the value change animation in milliseconds. Default is 0.
		//		The animation occurs on both user interactions and programmatic value changes.
		//		Set this property to 0 to disable animation.
		animationDuration: 0,

		// animationEaser: Object
		//		The easer function of the value change animation. Default is fx._defaultEasing.
		animationEaser: null,

		_indicatorShapeFuncFlag: true,

		_interactionAreaFlag: true,

		_downListeners: null,

		_cursorListeners: null,

		_moveAndUpListeners: null,

		_transitionValue: NaN,

		_preventAnimation: false,

		_animation: null,

		constructor: function(){

			// watches changed happening on the "value" property to call this.valueChanged() function which
			// can be listen by user with connect.connect
			this.watch("value", lang.hitch(this, function(){
				this.valueChanged(this);
			}));
			this.watch("value", lang.hitch(this, this._startAnimation));

			this.watch("interactionArea", lang.hitch(this, function(){
				this._interactionAreaFlag = true;
			}));
			this.watch("interactionMode", lang.hitch(this, function(){
				this._interactionAreaFlag = true;
			}));

			this.watch("indicatorShapeFunc", lang.hitch(this, function(){
				this._indicatorShapeFuncFlag = true;
			}));

			this.addInvalidatingProperties(["scale", "value", "indicatorShapeFunc", "interactionArea", "interactionMode"]);

			this._downListeners = [];
			this._moveAndUpListeners = [];
			this._cursorListeners = [];
		},

		_startAnimation: function(prop, oldValue, newValue){
			// summary:
			//		Internal method.
			// tags:
			//		private
			if(this.animationDuration == 0){
				return;
			}
			if(this._animation && (this._preventAnimation || oldValue == newValue)){
				this._animation.stop();
				return;
			}
			this._animation = new fx.Animation({curve: [oldValue, newValue],
										duration: this.animationDuration,
										easing: this.animationEaser ? this.animationEaser : fx._defaultEasing,
										onAnimate: lang.hitch(this, this._updateAnimation),
										onEnd: lang.hitch(this, this._endAnimation),
										onStop: lang.hitch(this, this._endAnimation)});

			this._animation.play();
		},

		_updateAnimation: function(v){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._transitionValue = v;
			this.invalidateRendering();
		},

		_endAnimation: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._transitionValue = NaN;
			this.invalidateRendering();
		},

		refreshRendering: function(){
			if(this._gfxGroup == null || this.scale == null){
				return;
			}else{
				if(this._indicatorShapeFuncFlag && lang.isFunction(this.indicatorShapeFunc)){
					this._gfxGroup.clear();
					this.indicatorShapeFunc(this._gfxGroup, this);
					this._indicatorShapeFuncFlag = false;
				}

				if(this._interactionAreaFlag){
					this._interactionAreaFlag = this._connectDownListeners();
				}
			}
		},

		valueChanged: function(indicator){
			// summary:
			//		Invoked when the value of the indicator changes.
			//		User can connect an listener on this function:
			// |	connect.connect(theIndicator, "valueChanged", lang.hitch(this, function(){
			// |		//do something
			// |	}));
			on.emit(this, "valueChanged", {
				target: this,
				bubbles: true,
				cancelable: true
			});
		},

		_disconnectDownListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			for(var i = 0; i < this._downListeners.length; i++){
				connect.disconnect(this._downListeners[i]);
			}
			this._downListeners = [];
		},

		_disconnectMoveAndUpListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			for(var i = 0; i < this._moveAndUpListeners.length; i++){
				connect.disconnect(this._moveAndUpListeners[i]);
			}
			this._moveAndUpListeners = [];
		},

		_disconnectListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._disconnectDownListeners();
			this._disconnectMoveAndUpListeners();
			this._disconnectCursorListeners();
		},

		_connectCursorListeners: function(target){
			// summary:
			//		Internal method.
			// tags:
			//		private
			var listener = target.connect("onmouseover", this, function(){
				this.scale._gauge._setCursor("pointer");
			});
			this._cursorListeners.push(listener);
			listener = target.connect("onmouseout", this, function(event){
					this.scale._gauge._setCursor("");
				}
			);
			this._cursorListeners.push(listener);
		},

		_disconnectCursorListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			for(var i = 0; i < this._cursorListeners.length; i++){
				connect.disconnect(this._cursorListeners[i]);
			}
			this._cursorListeners = [];
		},

		_connectDownListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._disconnectDownListeners();
			this._disconnectCursorListeners();
			var listener = null;
			var downEventName;
			if(this.interactionMode == "mouse"){
				downEventName = "onmousedown";
			}else if(this.interactionMode == "touch"){
				downEventName = "ontouchstart";
			}

			if(this.interactionMode == "mouse" || this.interactionMode == "touch"){
				if(this.interactionArea == "indicator"){
					listener = this._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					if (this.interactionMode == "mouse") {
						this._connectCursorListeners(this._gfxGroup);
					}
				}else if(this.interactionArea == "gauge"){
					if(!this.scale || !this.scale._gauge || !this.scale._gauge._gfxGroup){
						return true;
					}
					listener = this.scale._gauge._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					listener = this._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					if (this.interactionMode == "mouse") {
						this._connectCursorListeners(this.scale._gauge._gfxGroup);
					}
				}else if(this.interactionArea == "area"){
					if(!this.scale || !this.scale._gauge || !this.scale._gauge._baseGroup){
						return true;
					}
					listener = this.scale._gauge._baseGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					listener = this._gfxGroup.connect(downEventName, this, this._onMouseDown);
					this._downListeners.push(listener);
					if (this.interactionMode == "mouse") {
						this._connectCursorListeners(this.scale._gauge._baseGroup);
					}
				}
			}
			return false;
		},

		_connectMoveAndUpListeners: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			var listener = null;
			var moveEventName;
			var upEventName;
			if(this.interactionMode == "mouse"){
				moveEventName = "onmousemove";
				upEventName = "onmouseup";
			}else if(this.interactionMode == "touch"){
				moveEventName = "ontouchmove";
				upEventName = "ontouchend";
			}

			listener = connect.connect(win.doc, moveEventName, this, this._onMouseMove);
			this._moveAndUpListeners.push(listener);

			listener = connect.connect(win.doc, upEventName, this, this._onMouseUp);
			this._moveAndUpListeners.push(listener);
		},

		_onMouseDown: function(event){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._connectMoveAndUpListeners();
			this._startEditing();
		},

		_onMouseMove: function(event){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._preventAnimation = true;
			if(this._animation){
				this._animation.stop();
			}
		},

		_onMouseUp: function(event){
			// summary:
			//		Internal method.
			// tags:
			//		private
			this._disconnectMoveAndUpListeners();
			this._preventAnimation = false;
			this._endEditing();
		},

		_startEditing: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			if(!this.scale || !this.scale._gauge){
				return;
			}else{
				this.scale._gauge.onStartEditing({
					indicator: this
				});
			}
		},

		_endEditing: function(){
			// summary:
			//		Internal method.
			// tags:
			//		private
			if(!this.scale || !this.scale._gauge){
				return;
			}else{
				this.scale._gauge.onEndEditing({
					indicator: this
				});
			}
		}

	});
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         pr0 filter
// @description  filters by tags
// @namespace    filter
// @version      1.0
// @author       cRYPTOR
// @match        https://pr0gramm.com/*
// @match        http://pr0gramm.com/*
// ==/UserScript==

$(document).ready(function(){

	// '0' : 'Schwuchtel',
	// '1' : 'Neuschwuchtel',
	// '2' : 'Altschwuchtel',
	// '3' : 'Admin',
	// '4' : 'Gesperrt',
	// '5' : 'Moderator',
	// '6' : 'Fliesentischbesitzer',
	// '7' : 'Lebende Legende',
	// '8' : 'pr0wichtler',
	// '9' : 'Edler Spender',

	var filterSettings = {
		tags: [],
		marks: [ 6 ],
		debug: false,
	};

	var call = function(callback, callbackData){
		if( callback != null){
			return callback(callbackData);
		}
	};

	var debugLog = function(str){
		if( filterSettings.debug){
			console.log(str);
		}
	};

	var searchObjects = [];
	var searchObjectFactory = {
		createSearchObject : function(content, callback){
			searchObjects[searchObjects.push({
				searchOptions:{
					'tags':content
				},
				min: 0,
				max: 0,
				ids: [],
				is: function(id){
					if( id > this.max )
						return false;
					if( id < this.min )
						debugLog('error!');
					return ( this.ids.indexOf(id) != -1 );
				},
				needsUpdate: function(id){
					return id < this.min;
				},
				update: function(callback, callbackData){

					var o = this;
					o.searchOptions.flags = p.user.flags

					p.api.get('items.get', o.searchOptions, function(data){
						if( data.items.length > 0){

							o.max = 0;
							o.min = Number.MAX_VALUE;

							$(data.items).each(function(index){
								var id = data.items[index].id;

								if( id > o.max)
									o.max = id;
								if( id < o.min)
									o.min = id;

								o.ids.push(id);

							});

							o.searchOptions.older = o.min;
							debugLog(o.searchOptions.older);
						}else{
							o.min = -1;
						}
						call(callback, callbackData);
					});
				}
			}) - 1].update(callback);
		}
	};

	var updateAll = function(inData){
		var isUp2Date = true;
		$(inData.data.items).each(function(itemIndex){
			if( !isUp2Date){
				return;
			}
			$(searchObjects).each(function(index){
				if( !isUp2Date){
					return;
				}
				var o = searchObjects[index];
				if( o.needsUpdate(inData.data.items[itemIndex].id)){
					isUp2Date = false;
					o.update(updateAll, inData);
				}
			});
		});
		if( isUp2Date ){
			call(inData.callback);
		}
	};

	p.Stream.prototype._load = function(options, callback){

		var stream = this;
		options.flags = p.user.flags;

		p.api.get('items.get', p.merge(options, this.options), function (data){
			updateAll({
				'data' : data,
				'callback' : function(){

					if(searchObjects.length > 0){
						data.items = $.grep(data.items, function(el, i){
							var alive = true;
							$(searchObjects).each(function(index){
								if( searchObjects[index].is(el.id) ){
									return alive = false;
								}
							});
							return alive;
						});
					}

					if( filterSettings.marks.length > 0 ){
						data.items = $.grep(data.items, function(el, i){
							return filterSettings.marks.indexOf(el.mark) == -1;
						});
					}

					var position = stream._processResponse(data);
					callback(data.items, position, data.error);
				}
			});
		});
	};

	if( filterSettings.tags.length == 0){
		p.Stream.prototype._load();
	}else{
		var count = 0;
		$(filterSettings.tags).each(function(index){
			searchObjectFactory.createSearchObject(filterSettings.tags[index], function(){
				++count;
				if( filterSettings.tags.length == count){
					p.Stream.prototype._load();
				}
			});
		});
	}
});<commit_msg>PR0-0003<commit_after>// ==UserScript==
// @name         pr0 filter
// @description  filters by tags
// @namespace    filter
// @version      1.1
// @author       cRYPTOR
// @match        https://pr0gramm.com/*
// @match        http://pr0gramm.com/*
// @grant		 none
// ==/UserScript==

$(document).ready(function(){

	var filterSettings = {
		tags: ['repost', 'wichtel', 'star wars'],
		marks: {
			'0':0, // Schwuchtel
			'1':0, // Neuschwuchtel
			'2':0, // Altschwuchtel
			'3':0, // Admin
			'4':0, // Gesperrt
			'5':0, // Moderator
			'6':1, // Fliesentischbesitzer
			'7':0, // Lebende Legende
			'8':0, // pr0wichtler
			'9':0, // Edler Spender
		},
		debug: false,
	};

	var call = function(callback, callbackData){
		if( callback != null){
			return callback(callbackData);
		}
	};

	var debugLog = function(str){
		if( filterSettings.debug){
			console.log(str);
		}
	};

	var searchObjects = [];
	var searchObjectFactory = {
		createSearchObject : function(content, callback){
			searchObjects[searchObjects.push({
				searchOptions:{
					'tags':content
				},
				min: 0,
				max: 0,
				ids: {},
				is: function(id){
					if( id > this.max )
						return false;
					if( id < this.min )
						debugLog('error!');
					return this.ids[id] != undefined;
				},
				needsUpdate: function(id){
					return id < this.min;
				},
				update: function(callback, callbackData){

					var o = this;
					o.searchOptions.flags = p.user.flags

					p.api.get('items.get', o.searchOptions, function(data){
						if( data.items.length > 0){

							o.max = 0;
							o.min = Number.MAX_VALUE;

							$(data.items).each(function(iIndex, i){
								var id = i.id;
								if( id > o.max)
									o.max = id;
								if( id < o.min)
									o.min = id;
								o.ids[id] = 1;
							});

							o.searchOptions.older = o.min;
							debugLog(o.searchOptions.older);
						}else{
							o.min = -1;
						}
						call(callback, callbackData);
					});
				}
			}) - 1].update(callback);
		}
	};

	var updateAll = function(inData){
		var up2Date = [];

		if( searchObjects.length > 0){
			$(inData.data.items).each(function(iIndex, i){

				$(searchObjects).each(function(sOIndex, sO){

					if( up2Date[sOIndex] != undefined ||
					 	!sO.needsUpdate(i.id) ){
						return;
					}

					up2Date[sOIndex] = 1;
					sO.update(updateAll, inData);
				});

				if( up2Date.length == searchObjects.length){
					return false;
				}
			});
		}

		if( up2Date.length == 0 ){
			call(inData.callback);
		}
	};

	p.Stream.prototype._load = function(options, callback){

		var stream = this;
		options.flags = p.user.flags;

		p.api.get('items.get', p.merge(options, this.options), function (data){
			updateAll({
				'data' : data,
				'callback' : function(){

					var oldLength = data.items.length;

					data.items = $.grep(data.items, function(el, i){
						return filterSettings.marks[el.mark] == 0;
					});

					if(searchObjects.length > 0){
						data.items = $.grep(data.items, function(el, i){
							var keep = true;
							$(searchObjects).each(function(sOIndex, sO){
								if( sO.is(el.id) ){
									return keep = false;
								}
							});
							return keep;
						});
					}

					var dif = oldLength - data.items.length;
					if( dif > 0 ){
						debugLog('filtered ' + dif + ' items!');
					}

					var position = stream._processResponse(data);
					callback(data.items, position, data.error);
				}
			});
		});
	};

	var softReload = function(){

		if( p.currentView.loadInProgress ){
			setTimeout(softReload,10);
			return;
		}

		var $currentItem = p.currentView.$currentItem;
		if($currentItem != undefined){
			p.currentView.hideItem();
		}

		p.currentView.show({});

		if($currentItem != undefined){
			$currentItem = $('#' + $currentItem[0].id);
			if($currentItem[0] != undefined){
				p.currentView.showItem($currentItem, 1);
			}
		}
	}

	if(filterSettings.tags > 0){
		var count = 0;
		$(filterSettings.tags).each(function(i,t){
			searchObjectFactory.createSearchObject(t, function(){
				++count;
				if( count == searchObjects.length){
					softReload();
				}
			});
		});
	}else{
		softReload();
	}
});<|endoftext|>