<commit_before>'use strict';

const logger = require('../logger');
const { FEATURE_CREATED, FEATURE_UPDATED, FEATURE_ARCHIVED } = require('../event-type');
const NameExistsError = require('../error/name-exists-error');
const NotFoundError = require('../error/notfound-error');
const ValidationError = require('../error/validation-error.js');
const validateRequest = require('../error/validate-request');
const extractUser = require('../extract-user');

const legacyFeatureMapper = require('../data-helper/legacy-feature-mapper');
const version = 1;

const handleErrors = (req, res, error) => {
    switch (error.constructor) {
        case NotFoundError:
            return res
                .status(404)
                .end();
        case NameExistsError:
            return res
                .status(403)
                .json([{ msg: `A feature named '${req.body.name}' already exists.` }])
                .end();
        case ValidationError:
            return res
                .status(400)
                .json(req.validationErrors())
                .end();
        default:
            logger.error('Server failed executing request', error);
            return res
                .status(500)
                .end();
    }
};

module.exports = function (app, config) {
    const { featureToggleStore, eventStore } = config.stores;

    app.get('/features', (req, res) => {
        featureToggleStore.getFeatures()
            .then((features) => features.map(legacyFeatureMapper.addOldFields))
            .then(features => res.json({ version, features }));
    });

    app.get('/features/:featureName', (req, res) => {
        featureToggleStore.getFeature(req.params.featureName)
            .then(legacyFeatureMapper.addOldFields)
            .then(feature => res.json(feature).end())
            .catch(() => res.status(404).json({ error: 'Could not find feature' }));
    });

    app.post('/features-validate', (req, res) => {
        req.checkBody('name', 'Name is required').notEmpty();
        req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i);

        validateRequest(req)
            .then(validateFormat)
            .then(validateUniqueName)
            .then(() => res.status(201).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.post('/features', (req, res) => {
        req.checkBody('name', 'Name is required').notEmpty();
        req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i);
        const userName = extractUser(req);

        validateRequest(req)
            .then(validateFormat)
            .then(validateUniqueName)
            .then((_req) => legacyFeatureMapper.toNewFormat(_req.body))
            .then(validateStrategy)
            .then((featureToggle) => eventStore.store({
                type: FEATURE_CREATED,
                createdBy: userName,
                data: featureToggle,
            }))
            .then(() => res.status(201).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.put('/features/:featureName', (req, res) => {
        const featureName = req.params.featureName;
        const userName = extractUser(req);
        const updatedFeature = legacyFeatureMapper.toNewFormat(req.body);

        updatedFeature.name = featureName;

        featureToggleStore.getFeature(featureName)
            .then(() => validateStrategy(updatedFeature))
            .then(() => eventStore.store({
                type: FEATURE_UPDATED,
                createdBy: userName,
                data: updatedFeature,
            }))
            .then(() => res.status(200).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.delete('/features/:featureName', (req, res) => {
        const featureName = req.params.featureName;
        const userName = extractUser(req);

        featureToggleStore.getFeature(featureName)
            .then(() => eventStore.store({
                type: FEATURE_ARCHIVED,
                createdBy: userName,
                data: {
                    name: featureName,
                },
            }))
            .then(() => res.status(200).end())
            .catch(error => handleErrors(req, res, error));
    });

    function validateUniqueName (req) {
        return new Promise((resolve, reject) => {
            featureToggleStore.getFeature(req.body.name)
                .then(() => reject(new NameExistsError('Feature name already exist')))
                .catch(() => resolve(req));
        });
    }

    function validateFormat (req) {
        if (req.body.strategy && req.body.strategies) {
            return Promise.reject(new ValidationError('Cannot use both "strategy" and "strategies".'));
        }

        return Promise.resolve(req);
    }

    function validateStrategy (featureToggle) {
        if (!featureToggle.strategies || featureToggle.strategies.length === 0) {
            return Promise.reject(new ValidationError('You must define at least one strategy'));
        }

        return Promise.resolve(featureToggle);
    }
};
<commit_msg>add toggle endpoint<commit_after>'use strict';

const logger = require('../logger');
const { FEATURE_CREATED, FEATURE_UPDATED, FEATURE_ARCHIVED } = require('../event-type');
const NameExistsError = require('../error/name-exists-error');
const NotFoundError = require('../error/notfound-error');
const ValidationError = require('../error/validation-error.js');
const validateRequest = require('../error/validate-request');
const extractUser = require('../extract-user');

const legacyFeatureMapper = require('../data-helper/legacy-feature-mapper');
const version = 1;

const handleErrors = (req, res, error) => {
    switch (error.constructor) {
        case NotFoundError:
            return res
                .status(404)
                .end();
        case NameExistsError:
            return res
                .status(403)
                .json([{ msg: `A feature named '${req.body.name}' already exists.` }])
                .end();
        case ValidationError:
            return res
                .status(400)
                .json(req.validationErrors())
                .end();
        default:
            logger.error('Server failed executing request', error);
            return res
                .status(500)
                .end();
    }
};

module.exports = function (app, config) {
    const { featureToggleStore, eventStore } = config.stores;

    app.get('/features', (req, res) => {
        featureToggleStore.getFeatures()
            .then((features) => features.map(legacyFeatureMapper.addOldFields))
            .then(features => res.json({ version, features }));
    });

    app.get('/features/:featureName', (req, res) => {
        featureToggleStore.getFeature(req.params.featureName)
            .then(legacyFeatureMapper.addOldFields)
            .then(feature => res.json(feature).end())
            .catch(() => res.status(404).json({ error: 'Could not find feature' }));
    });

    app.post('/features-validate', (req, res) => {
        req.checkBody('name', 'Name is required').notEmpty();
        req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i);

        validateRequest(req)
            .then(validateFormat)
            .then(validateUniqueName)
            .then(() => res.status(201).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.post('/features', (req, res) => {
        req.checkBody('name', 'Name is required').notEmpty();
        req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i);
        const userName = extractUser(req);

        validateRequest(req)
            .then(validateFormat)
            .then(validateUniqueName)
            .then((_req) => legacyFeatureMapper.toNewFormat(_req.body))
            .then(validateStrategy)
            .then((featureToggle) => eventStore.store({
                type: FEATURE_CREATED,
                createdBy: userName,
                data: featureToggle,
            }))
            .then(() => res.status(201).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.put('/features/:featureName', (req, res) => {
        const featureName = req.params.featureName;
        const userName = extractUser(req);
        const updatedFeature = legacyFeatureMapper.toNewFormat(req.body);

        updatedFeature.name = featureName;

        featureToggleStore.getFeature(featureName)
            .then(() => validateStrategy(updatedFeature))
            .then(() => eventStore.store({
                type: FEATURE_UPDATED,
                createdBy: userName,
                data: updatedFeature,
            }))
            .then(() => res.status(200).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.put('/features/:featureName/toggle', (req, res) => {
        const featureName = req.params.featureName;
        const userName = extractUser(req);

        featureToggleStore.getFeature(featureName)
            .then((feature) => {
                feature.enabled = !feature.enabled;
                return eventStore.store({
                    type: FEATURE_UPDATED,
                    createdBy: userName,
                    data: feature,
                });
            })
            .then(() => res.status(200).end())
            .catch(error => handleErrors(req, res, error));
    });

    app.delete('/features/:featureName', (req, res) => {
        const featureName = req.params.featureName;
        const userName = extractUser(req);

        featureToggleStore.getFeature(featureName)
            .then(() => eventStore.store({
                type: FEATURE_ARCHIVED,
                createdBy: userName,
                data: {
                    name: featureName,
                },
            }))
            .then(() => res.status(200).end())
            .catch(error => handleErrors(req, res, error));
    });

    function validateUniqueName (req) {
        return new Promise((resolve, reject) => {
            featureToggleStore.getFeature(req.body.name)
                .then(() => reject(new NameExistsError('Feature name already exist')))
                .catch(() => resolve(req));
        });
    }

    function validateFormat (req) {
        if (req.body.strategy && req.body.strategies) {
            return Promise.reject(new ValidationError('Cannot use both "strategy" and "strategies".'));
        }

        return Promise.resolve(req);
    }

    function validateStrategy (featureToggle) {
        if (!featureToggle.strategies || featureToggle.strategies.length === 0) {
            return Promise.reject(new ValidationError('You must define at least one strategy'));
        }

        return Promise.resolve(featureToggle);
    }
};
<|endoftext|>


<commit_before>/*

browserclient.js
================

Model objects (via Backbone) for a browser client (a connection to a browser + the test run session)
and the returned test results for a run of tests.

*/

var log = require('winston')
  , Backbone = require('backbone')
  , tap = require('tap')

var TestResults = Backbone.Model.extend({
    initialize: function(){
        this.reset()
    }
    , reset: function(){
        this.set({
            topLevelError: null
            , failed: 0
            , passed: 0
            , total: 0
            , tests: new Backbone.Collection()
            , all: false
        })
    }
    , addResult: function(result){
        var total = this.get('total')
          , passed = this.get('passed')
          , failed = this.get('failed')
        total++
        if (result.failed == 0)
            passed++
        else
            failed++
        this.set({
            total: total
            , passed: passed
            , failed: failed
            , items: result.items
        })
        this.get('tests').push(result)
    }
})

exports.TestResults = TestResults

var BrowserClient = Backbone.Model.extend({
    defaults: {
        type: 'browser'
    }
    , initialize: function(attrs){
        var client = this.client = attrs.client
        this.app = attrs.app
        this.server = this.app.server
        this.set({
            name: null
            , logOutputChunks: new Backbone.Collection
            , results: new TestResults
        })
        var self = this
          , server = this.server
          , results = this.get('results')
          , logOutputChunks = this.get('logOutputChunks')

        client.on('top-level-error', function(msg, url, line){
            var message = msg + ' at ' + url + ', line ' + line
            logOutputChunks.push({
                type: 'error'
                , text: message
            })
        })
        client.on('error', function(message){
            logOutputChunks.push({
                type: 'error'
                , text: message
            })
        })
        client.on('warn', function(message){
            logOutputChunks.push({
                type: 'warn'
                , text: message
            })
        })
        client.on('log', function(message){
            logOutputChunks.push({
                type: 'log'
                , text: message
            })
        })
        client.on('browser-login', function(browserName){
            self.set('name', browserName)
            log.info('browser-login ' + browserName)
        })
        client.on('tests-start', function(){
            self.trigger('tests-start')
        })
        client.on('test-result', function(result){
            results.addResult(result)
            self.server.emit('test-result', result)
        })
        client.on('all-test-results', function(){
            results.set('all', true)
            self.trigger('tests-end')
            self.server.emit('all-test-results', results, self)
        })
        client.on('disconnect', function(){
            server.removeBrowser(self)
        })

    }
    , startTests: function(){
        this.get('results').reset()
        this.client.emit('start-tests')
    }
})

exports.BrowserClient = BrowserClient

var ProcessClient = Backbone.Model.extend({
    defaults: {
        type: 'process'
    }
    , initialize: function(attrs){
        this.launcher = attrs.launcher
        this.app = attrs.app
        // Assume launcher has already launched
        this.set({
            name: this.launcher.name
            , logOutputChunks: new Backbone.Collection
            , results: this.isTap() ? new TestResults : null
        })

        this.startTests()
    }
    , isTap: function(){
        return this.launcher.settings.protocol === 'tap'
    }
    , registerProcess: function(){
        var process = this.launcher.process
        var stdout = process.stdout
        var stderr = process.stderr
        var self = this
        stdout.on('data', function(data){
            data.toString().split(/\r?\n/).forEach(function(line){
                if (line.trim().length > 0){
                    self.get('logOutputChunks').push({
                        type: 'log'
                        , text: line
                    })
                }
            })
        })
        stderr.on('data', function(data){
            log.info('stderr: ' + data)
            data.toString().split(/\r?\n/).forEach(function(line){
                if (line.trim().length > 0){
                    self.get('logOutputChunks').push({
                        type: 'error'
                        , text: line
                    })
                }
            })
        })
        process.on('exit', function(code){
            self.set('allPassed', code === 0)
            self.trigger('tests-end')
        })
        if (this.isTap()){
            this.setupTapConsumer()
        }
    }
    , setupTapConsumer: function(){
        var stdout = this.launcher.process.stdout
        this.tapConsumer = new tap.Consumer
        this.tapConsumer.on('data', this.onTapData.bind(this))
        this.tapConsumer.on('end', this.onTapEnd.bind(this))
        this.tapConsumer.on('bailout', this.onTapError.bind(this))
        stdout.pipe(this.tapConsumer)
    }
    , onTapData: function(data){
        if (typeof data === 'string') return
        try{
            var results = this.get('results')

            if (data.id === undefined) {
                return
            }

            var test = {
                passed: 0
                , failed: 0
                , total: 1
                , id: data.id
                , name: data.name
                , items: []
            }

            if (!data.ok) {
                test.items.push({
                    passed: false
                    , message: data.name
                    , stacktrace: data.stack && data.stack.join("\n")
                })
                test.failed++
            } else {
                test.passed++
            }

            results.addResult(test)
        }catch(e){
            log.error(e.message)
            log.error(e.stack)
        }
    }
    , onTapError: function(){
        log.info('tap parser bailing out')
        this.set('results', null)
    }
    , onTapEnd: function(err, testCount){
        log.info('onTapEnd, testCount: ' + testCount)
        var results = this.get('results')
        results.set('all', true)
        this.tapConsumer.removeAllListeners()
        this.tapConsumer = null
    }
    , startTests: function(){
        var self = this
        if (this.get('results')){
            this.get('results').reset()
        }else{
            this.set('results', this.isTap() ? new TestResults : null)
        }
        this.get('logOutputChunks').reset([])
        this.set('allPassed', undefined)
        this.launcher.launch()
        this.registerProcess()
        setTimeout(function(){
            self.trigger('tests-start')
        }, 1)
    }
})

exports.ProcessClient = ProcessClient

<commit_msg>Added launcher config option to hide stdout or stderr from display.<commit_after>/*

browserclient.js
================

Model objects (via Backbone) for a browser client (a connection to a browser + the test run session)
and the returned test results for a run of tests.

*/

var log = require('winston')
  , Backbone = require('backbone')
  , tap = require('tap')

var TestResults = Backbone.Model.extend({
    initialize: function(){
        this.reset()
    }
    , reset: function(){
        this.set({
            topLevelError: null
            , failed: 0
            , passed: 0
            , total: 0
            , tests: new Backbone.Collection()
            , all: false
        })
    }
    , addResult: function(result){
        var total = this.get('total')
          , passed = this.get('passed')
          , failed = this.get('failed')
        total++
        if (result.failed == 0)
            passed++
        else
            failed++
        this.set({
            total: total
            , passed: passed
            , failed: failed
            , items: result.items
        })
        this.get('tests').push(result)
    }
})

exports.TestResults = TestResults

var BrowserClient = Backbone.Model.extend({
    defaults: {
        type: 'browser'
    }
    , initialize: function(attrs){
        var client = this.client = attrs.client
        this.app = attrs.app
        this.server = this.app.server
        this.set({
            name: null
            , logOutputChunks: new Backbone.Collection
            , results: new TestResults
        })
        var self = this
          , server = this.server
          , results = this.get('results')
          , logOutputChunks = this.get('logOutputChunks')

        client.on('top-level-error', function(msg, url, line){
            var message = msg + ' at ' + url + ', line ' + line
            logOutputChunks.push({
                type: 'error'
                , text: message
            })
        })
        client.on('error', function(message){
            logOutputChunks.push({
                type: 'error'
                , text: message
            })
        })
        client.on('warn', function(message){
            logOutputChunks.push({
                type: 'warn'
                , text: message
            })
        })
        client.on('log', function(message){
            logOutputChunks.push({
                type: 'log'
                , text: message
            })
        })
        client.on('browser-login', function(browserName){
            self.set('name', browserName)
            log.info('browser-login ' + browserName)
        })
        client.on('tests-start', function(){
            self.trigger('tests-start')
        })
        client.on('test-result', function(result){
            results.addResult(result)
            self.server.emit('test-result', result)
        })
        client.on('all-test-results', function(){
            results.set('all', true)
            self.trigger('tests-end')
            self.server.emit('all-test-results', results, self)
        })
        client.on('disconnect', function(){
            server.removeBrowser(self)
        })

    }
    , startTests: function(){
        this.get('results').reset()
        this.client.emit('start-tests')
    }
})

exports.BrowserClient = BrowserClient

var ProcessClient = Backbone.Model.extend({
    defaults: {
        type: 'process'
    }
    , initialize: function(attrs){
        this.launcher = attrs.launcher
        this.app = attrs.app
        // Assume launcher has already launched
        this.set({
            name: this.launcher.name
            , logOutputChunks: new Backbone.Collection
            , results: this.isTap() ? new TestResults : null
        })

        this.startTests()
    }
    , isTap: function(){
        return this.launcher.settings.protocol === 'tap'
    }
    , registerProcess: function(){
        var settings = this.launcher.settings
        var process = this.launcher.process
        var stdout = process.stdout
        var stderr = process.stderr
        var self = this

        if (!settings.hide_stdout){
            stdout.on('data', function(data){
                data.toString().split(/\r?\n/).forEach(function(line){
                    if (line.trim().length > 0){
                        self.get('logOutputChunks').push({
                            type: 'log'
                            , text: line
                        })
                    }
                })
            })
        }
        if (!settings.hide_stderr){
            stderr.on('data', function(data){
                log.info('stderr: ' + data)
                data.toString().split(/\r?\n/).forEach(function(line){
                    if (line.trim().length > 0){
                        self.get('logOutputChunks').push({
                            type: 'error'
                            , text: line
                        })
                    }
                })
            })
        }
        process.on('exit', function(code){
            self.set('allPassed', code === 0)
            self.trigger('tests-end')
        })
        if (this.isTap()){
            this.setupTapConsumer()
        }
    }
    , setupTapConsumer: function(){
        var stdout = this.launcher.process.stdout
        this.tapConsumer = new tap.Consumer
        this.tapConsumer.on('data', this.onTapData.bind(this))
        this.tapConsumer.on('end', this.onTapEnd.bind(this))
        this.tapConsumer.on('bailout', this.onTapError.bind(this))
        stdout.pipe(this.tapConsumer)
    }
    , onTapData: function(data){
        if (typeof data === 'string') return
        try{
            var results = this.get('results')

            if (data.id === undefined) {
                return
            }

            var test = {
                passed: 0
                , failed: 0
                , total: 1
                , id: data.id
                , name: data.name
                , items: []
            }

            if (!data.ok) {
                test.items.push({
                    passed: false
                    , message: data.name
                    , stacktrace: data.stack && data.stack.join("\n")
                })
                test.failed++
            } else {
                test.passed++
            }

            results.addResult(test)
        }catch(e){
            log.error(e.message)
            log.error(e.stack)
        }
    }
    , onTapError: function(){
        log.info('tap parser bailing out')
        this.set('results', null)
    }
    , onTapEnd: function(err, testCount){
        log.info('onTapEnd, testCount: ' + testCount)
        var results = this.get('results')
        results.set('all', true)
        this.tapConsumer.removeAllListeners()
        this.tapConsumer = null
    }
    , startTests: function(){
        var self = this
        if (this.get('results')){
            this.get('results').reset()
        }else{
            this.set('results', this.isTap() ? new TestResults : null)
        }
        this.get('logOutputChunks').reset([])
        this.set('allPassed', undefined)
        this.launcher.launch()
        this.registerProcess()
        setTimeout(function(){
            self.trigger('tests-start')
        }, 1)
    }
})

exports.ProcessClient = ProcessClient

<|endoftext|>


<commit_before>var shovel = require('../shovel'),
    util = require('../util'),
    path = require('path'),
    temp = require('temp'),
    exec = require('child_process').exec,
    fs = require('fs'),
    chpldir = path.resolve(__dirname, '..', '..', 'frameworks', 'chapel'),
    cw2 = fs.readFileSync(path.resolve(chpldir, 'cw-2.chpl'));

module.exports.run = function run(opts, cb) {
  temp.track();
  const dir = temp.mkdirSync('chpl');
  shovel.start(opts, cb, {
    solutionOnly: function(runCode, fail) {
      if (opts.setup) handleSetup(opts, dir);
      const executable = path.join(dir, 'solution');
      const solutionFile = util.codeWriteSync('chpl', opts.solution, dir, 'solution.chpl');
      compile([solutionFile, '-o', executable], function(error, stdout, stderr) {
        if (error) return fail(error, stdout, stderr);
        opts.publish('stdout', stdout);
        runCode({name: executable, args: []});
      });
    },

    testIntegration: function(runCode, fail) {
      if (opts.setup) handleSetup(opts, dir);
      const executable = path.join(dir, 'solution');
      const solutionFile = util.codeWriteSync('chpl', [
        cw2,
        opts.solution,
        opts.fixture,
      ].join('\n'), dir, 'solution.chpl');
      compile([solutionFile, '-o', executable], function(error, stdout, stderr) {
        if (error) return fail(error, stdout, stderr);
        opts.publish('stdout', stdout);
        runCode({name: executable, args: []});
      });
    },

    sanitizeStdErr: function(error) {
         error = error || '';
         return error.replace(/\/tmp.*(solution\.chpl|solution)[:0-9]*/g, '')
                           .replace(/Error: Command failed:/g, '')
                           .replace('\n', '')
                           .replace('  ', ' ');
      }
  });
};

function compile(args, cb) {
  exec(`chpl ${args.join(' ')}`, cb);
}
<commit_msg>Chapel setup added<commit_after>var shovel = require('../shovel'),
    util = require('../util'),
    path = require('path'),
    temp = require('temp'),
    exec = require('child_process').exec,
    fs = require('fs'),
    chpldir = path.resolve(__dirname, '..', '..', 'frameworks', 'chapel'),
    cw2 = fs.readFileSync(path.resolve(chpldir, 'cw-2.chpl'));

module.exports.run = function run(opts, cb) {
  temp.track();
  const dir = temp.mkdirSync('chpl');
  shovel.start(opts, cb, {
    solutionOnly: function(runCode, fail) {
      if (opts.setup) handleSetup(opts, dir);
      const executable = path.join(dir, 'solution');
      const solutionFile = util.codeWriteSync('chpl', opts.solution, dir, 'solution.chpl');
      compile([solutionFile, '-o', executable], function(error, stdout, stderr) {
        if (error) return fail(error, stdout, stderr);
        opts.publish('stdout', stdout);
        runCode({name: executable, args: []});
      });
    },

    testIntegration: function(runCode, fail) {
      if (opts.setup) handleSetup(opts, dir);
      const executable = path.join(dir, 'solution');
      const solutionFile = util.codeWriteSync('chpl', [
        cw2,
        opts.solution,
        opts.fixture,
      ].join('\n'), dir, 'solution.chpl');
      compile([solutionFile, '-o', executable], function(error, stdout, stderr) {
        if (error) return fail(error, stdout, stderr);
        opts.publish('stdout', stdout);
        runCode({name: executable, args: []});
      });
    },

    sanitizeStdErr: function(error) {
         error = error || '';
         return error.replace(/\/tmp.*(solution\.chpl|solution)[:0-9]*/g, '')
                           .replace(/Error: Command failed:/g, '')
                           .replace(/error: uncaught error/g, '')
                           .replace('\n', '')
                           .replace('  ', ' ');
      }
  });
};

function compile(args, cb) {
  exec(`chpl ${args.join(' ')}`, cb);
}

function handleSetup(opts, dir) {
  opts.solution = opts.setup + '\n' + opts.solution;
}
<|endoftext|>


<commit_before>'use strict';

/*
// Not currently in use
angular.module('algorea')
  .directive('viewButton', function () {
    return {
      restrict: 'A',
      template: '<button type="button" class="btn btn-default btn-xs view-button" ng-click="toggleFullscreen();">'+
                  ''+
                '</button>',
  };
});
*/

// loosely based on http://stackoverflow.com/questions/32513974/affix-element-with-pure-angularjs
// Compute the absolute top of the element.
function getAbsoluteTop (element) {
  var top = 0;
  while (element) {
    top += element.offsetTop - element.scrollTop + element.clientTop;
    element = element.offsetParent;
  }
  return top;
}
// This directive sets the affix class on its single child element
// when the child's absolute top position is negative.
// The height of the container is also adjusted when the child is
// affixed to avoid changes to the page layout.
affixMeDirective.$inject = ['$window', '$timeout'];
function affixMeDirective ($window, $timeout) {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var isEnabled = false;
      function onScroll () {
        var child = element.children();
        var top = getAbsoluteTop(element[0]);
        scope.top = top;
        if (top >= 0) {
          child.removeClass('affix');
        } else {
          child.addClass('affix');
          element.css('height', child[0].offsetHeight);
        }
      }
      function enable () {
        if (!isEnabled) {
          $timeout(onScroll, 0);
          $window.addEventListener("scroll", onScroll);
          isEnabled = true;
        }
      }
      function disable () {
        if (isEnabled) {
          $window.removeEventListener("scroll", onScroll);
          var child = element.children();
          child.removeClass('affix');
          element.css('height', '');
          isEnabled = false;
        }
      }
      scope.$watch(attrs.affixMe, function (val) {
        if (val) enable(); else disable();
      });
      scope.$on('$destroy', disable);
    }
  };
}
angular.module('algorea').directive('affixMe', affixMeDirective);

// Menus states
angular.module('algorea')
   .factory('layoutService', ['$rootScope', function ($rootScope) {
      function reset () {
        $rootScope.affix = 'toolbar';
        $rootScope.showNavTopOverlay = false;
      }
      $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
        reset();
      });
      reset();
      return {
         openNavTopOverlay: function () {
            $rootScope.showNavTopOverlay = true;
         },
         closeNavTopOverlay: function () {
            $rootScope.showNavTopOverlay = false;
         },
         openSidebarLeftOverlay: function () {
            $rootScope.showSidebarLeftOverlay = true;
         },
         closeSidebarLeftOverlay: function () {
            $rootScope.showSidebarLeftOverlay = false;
         },
         openMobileTopMenu: function () {
            $rootScope.mobileNavTopIsOpen = true;
         },
         closeMobileTopMenu: function () {
            $rootScope.mobileNavTopIsOpen = false;
         },
         openMobileTopMenuOverlay: function () {
            $rootScope.showMobileNavTopOverlay = true;
         },
         closeMobileTopMenuOverlay: function () {
            $rootScope.showMobileNavTopOverlay = false;
         },
         affixToolbar: function () {
            $rootScope.affix = 'toolbar';
         },
         affixHeader: function () {
            $rootScope.affix = 'header';
         }
      };
   }]);

angular.module('algorea')
  .controller('layoutController', ['$scope', '$window', '$timeout', '$rootScope', '$interval', '$injector', 'itemService', 'pathService', '$state', 'layoutService', function ($scope, $window, $timeout, $rootScope, $interval, $injector, itemService, pathService, $state, layoutService) {
    var pane_west = $('.ui-layout-west');
    var pane_center = $('.ui-layout-center');
    var container = $('#layoutContainer');
    var taskMinWidth = 820;
    var nonTaskMinWidth = 400;
    var mapService = null;
    if (config.domains.current.useMap) {
      mapService = $injector.get('mapService');
      mapService.setClickedCallback(function(path, lastItem) {
        if (lastItem.sType == 'Task' || lastItem.sType == 'Course') {
           var pathArray = path.split('/');
           var selr = pathArray.length;
           var sell = selr -1;
           var pathParams = pathService.getPathParams();
           if (pathParams.basePathStr == path) {
              $scope.layout.closeMap();
           } else {
              $state.go('contents', {path: path,sell:sell,selr:selr});
           }
        }
      });
    }
    $scope.mapInfos = {
       'mapPossible' : true,
       'hasMap':false,
       'mapOpened' : false
    };
   $scope.gotoIndex = function() {
      var defaultPathStr = config.domains.current.defaultPath;
      if (defaultPathStr.substr(0, 10) == '/contents/') {
         defaultPathStr = defaultPathStr.substr(10, defaultPathStr.length);
         var pathArray = defaultPathStr.split('/');
         var selr = pathArray.length;
         var sell = selr -1;
      }
      $state.go('contents', {path: defaultPathStr,sell:null,selr:null});
   }
    // $scope.layout will be accesset and set by viewButton directive in a subscope, so
    // it must be an object, or prototypal inheritance will mess everything
    $scope.layout = {
      west_is_open : true,
      tablesResized: function() {
      },
      refreshSizes : function() {

      },
      buttonClass: "fullscreen",
      state: "normal",
      navTopIsOpen: true,
      goFullscreen: function() {

      },
      goNormal: function() {

      },
      hasMap: function(hasMap, firstOpening) {
         $scope.mapInfos.hasMap = hasMap;
         if (!config.domains.current.useMap) return;
         if (hasMap == 'always') {
            $scope.layout.openMap();
         } else if (hasMap == 'never') {
            $scope.layout.closeMap();
         } else if (hasMap == 'button' && firstOpening) {
            $scope.layout.closeMap();
         }
      },
      openMap: function() {
         if (!$scope.mapInfos.mapMode && config.domains.current.useMap) {
            if ($scope.mapInfos.hasMap == 'button') {
               $scope.layout.navTopIsOpen = true;
            }
            $scope.mapInfos.mapMode = true;
            //$('#footer').hide();
            $('#view-right').hide();
            $('#map').show();
            mapService.show();
         }
      },
      closeMap: function() {
         if ($scope.mapInfos.mapMode) {
            if ($scope.mapInfos.hasMap == 'button') {
               $scope.layout.navTopIsOpen = false;
            }
            //$('#footer').show();
            $('#view-right').show();
            $('#map').hide();
            $scope.mapInfos.mapMode = false;
         }
      },
      openSidebarLeft: function() {
        $rootScope.sidebarLeftIsOpen = true;
        layoutService.openSidebarLeftOverlay(); // is hidden in CSS when not mobile layout to enable its visibility when resizing window
        $scope.layout.closeNavTopOverlay();
        if ($rootScope.isMobileLayout) {
          $scope.layout.closeMobileNavTop();
        }
      },
      closeSidebarLeft: function() {
        $rootScope.sidebarLeftIsOpen = false;
        layoutService.closeSidebarLeftOverlay();
      },
      closeSidebarLeftOverlay: function() {
        $scope.layout.closeSidebarLeft();
        layoutService.closeSidebarLeftOverlay();
      },
      openTaskMenu: function() {
         layoutService.affixHeader();
         layoutService.openNavTopOverlay();
         $scope.layout.navTopIsOpen = true;
      },
      openMobileTaskMenu: function() {
        layoutService.openNavTopOverlay();
        $scope.layout.navTopIsOpen = true;
        layoutService.openMobileTopMenu();
      },
      closeMobileTaskMenu: function() {
        layoutService.closeNavTopOverlay();
        $scope.layout.navTopIsOpen = false;
        layoutService.closeMobileTopMenu();
      },
      closeNavTopOverlay: function() {
        if ($rootScope.showNavTopOverlay) {
          layoutService.closeNavTopOverlay();
          layoutService.affixToolbar();
          $scope.layout.navTopIsOpen = false;
        }
      },
      openMobileNavTop: function() {
        layoutService.openMobileTopMenuOverlay();
        layoutService.openMobileTopMenu();
        if ($rootScope.isMobileLayout) {
          $scope.layout.closeSidebarLeft();
        }
      },
      closeMobileNavTop: function() {
        layoutService.closeMobileTopMenuOverlay();
        layoutService.closeMobileTopMenu();
      },
      breadcrumbsClicked: function(event) {
         // do not close menu when user clicks on link
         if (!config.domains.current.clickableMenu || event.target.parentNode.parentNode.className.indexOf('breadcrumbs-item') != -1 || event.target.className.indexOf('link-arrow') != -1) {
            return;
         }
      },
      toggleRight: function() {
         $('#sidebar-right').toggleClass('sidebar-right-toggled');
         $('#main-titlebar-community').toggleClass('main-titlebar-community-toggled');
         $scope.layout.refreshSizes();
      },
      setRightIcon: function() {
         if ($('#sidebar-right').hasClass('sidebar-right-toggled')) {
            $('#main-titlebar-community').addClass('main-titlebar-community-toggled');
         }
      },
      closeRight: function() {
         $scope.layout.rightOpen = false;
         if ($(window).width() < 1100) {
            if ($('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.toggleRight();
            }
         } else {
            if (!$('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.toggleRight();
            }
         }
      },
      openRight: function() {
         if ($(window).width() < 1100) {
            if (!$('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.rightOpen = true;
               $scope.layout.toggleRight();
            }
         } else {
            if ($('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.rightOpen = true;
               $scope.layout.toggleRight();
            }
         }
      }
    };
   $scope.layout.rightIsFullScreen = function(rightIsFullScreen) {
      if (rightIsFullScreen == $rootScope.rightIsFullScreen) {
         return;
      }
      $rootScope.rightIsFullScreen = rightIsFullScreen;
       $scope.layout.navTopIsOpen = true;
       if (rightIsFullScreen) {
         if (!$scope.mapInfos.mapMode) {
           $scope.layout.navTopIsOpen = false;
         }
        $scope.layout.closeRight();
        $scope.layout.closeSidebarLeft();
       }
       $scope.layout.refreshSizes();
    };
    var isCurrentlyOnePage = !config.domains.current.useLeftNavigation;
    $scope.layout.isOnePage = function(isOnePage) {
       if (config.domains.current.useLeftNavigation) {
          if (typeof isOnePage === 'undefined') {
             return isCurrentlyOnePage || $rootScope.mapInfos.mapMode;
          }
          isCurrentlyOnePage = isOnePage;
       } else {
          return true;
       }
    };
    // inspired from https://github.com/capaj/ng-tools/blob/master/src/debounce.js
    // used on onresize for obvious performance reasons
    function debounce(fn, timeout, apply){
       apply = angular.isUndefined(apply) ? true : apply;
       var nthCall = 0;
       return function(){ // intercepting fn
          var that = this;
          var argz = arguments;
          nthCall++;
          var later = (function(version){
             return function(){
                if (version === nthCall){
                   return fn.apply(that, argz);
                }
             };
          })(nthCall);
          return $timeout(later, timeout, apply);
       };
    }
    $scope.layout.separateEditorOK = false;
    var lastSeparateEditorOK = false;
    $scope.layout.refreshSizes = function() {
       if ($rootScope.rightIsFullScreen) { // things are handled automatically for everything but the task layout
          var availableMainWidth = $('#main-area').width();
          var minWidth = $('#task-right').css('min-width');
          if (!minWidth) {minWidth = '0px';}
          minWidth = parseInt(minWidth.slice(0,-2));
          if (!minWidth) {minWidth = 800;}
          if (availableMainWidth - 2*minWidth > 40) {
            $scope.layout.separateEditorOK = true;
          } else {
            $scope.layout.separateEditorOK = false;
          }
         if (lastSeparateEditorOK != $scope.layout.separateEditorOK) {
            $timeout($rootScope.apply);
         }
         lastSeparateEditorOK = $scope.layout.separateEditorOK;
       } else {
         $scope.layout.separateEditorOK = false;
       }
    };

    var lastWindowWidth = $(window).width();
    $scope.layout.onResize = function () {
       // reset the opened/closed status of the left sidebar depending on the
       // new window width
       var newWindowWidth = $(window).width();
       lastWindowWidth = newWindowWidth;

       $scope.layout.refreshSizes();
       $rootScope.isMobileLayout = newWindowWidth < 700 ? true : false;
    }
    // resizing on window resizing (tamed)
    $window.onresize = debounce($scope.layout.onResize, 100, false);
    // function to be called at sync end by the service (it's alive. It's alive...)
    $rootScope.refreshSizes = $scope.layout.refreshSizes;
    // resizing on state change
    $rootScope.$on('$viewContentLoaded', function() {
      $rootScope.hasSidebarLeft = $state.current.views.left.template.length > 1 ? true : false;
      if ($rootScope.hasSidebarLeft && !$rootScope.isMobileLayout) {$scope.layout.openSidebarLeft();}
      $timeout($scope.layout.refreshSizes, 0); // 100 works here, might have to be changed for slow computers
    });
    $interval($scope.layout.refreshSizes, 1000);
    $scope.$on('layout.taskLayoutChange', $scope.layout.refreshSizes);
    $rootScope.sidebarLeftIsOpen = true;
    $rootScope.showSidebarLeftOverlay = false;
    $rootScope.mobileNavTopIsOpen = false;
    $rootScope.isMobileLayout = $(window).width() < 700 ? true : false;
}]);
<commit_msg>fix toolbar affix<commit_after>'use strict';

/*
// Not currently in use
angular.module('algorea')
  .directive('viewButton', function () {
    return {
      restrict: 'A',
      template: '<button type="button" class="btn btn-default btn-xs view-button" ng-click="toggleFullscreen();">'+
                  ''+
                '</button>',
  };
});
*/

// loosely based on https://stackoverflow.com/questions/442404/retrieve-the-position-x-y-of-an-html-element
// Compute the absolute top of the element.
function getAbsoluteTop (element) {
  var top = 0;
  var rect = element.getBoundingClientRect();
  var bodyRect = document.body.getBoundingClientRect(),
      elemRect = element.getBoundingClientRect();
      top = elemRect.top;
  return top;
}
// This directive sets the affix class on its single child element
// when the child's absolute top position is negative.
// The height of the container is also adjusted when the child is
// affixed to avoid changes to the page layout.
affixMeDirective.$inject = ['$window', '$timeout'];
function affixMeDirective ($window, $timeout) {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var isEnabled = false;
      function onScroll () {
        var child = element.children();
        var top = getAbsoluteTop(element[0]);
        scope.top = top;
        if (top >= 0) {
          child.removeClass('affix');
        } else {
          child.addClass('affix');
          element.css('height', child[0].offsetHeight);
        }
      }
      function enable () {
        if (!isEnabled) {
          $timeout(onScroll, 0);
          $window.addEventListener("scroll", onScroll);
          isEnabled = true;
        }
      }
      function disable () {
        if (isEnabled) {
          $window.removeEventListener("scroll", onScroll);
          var child = element.children();
          child.removeClass('affix');
          element.css('height', '');
          isEnabled = false;
        }
      }
      scope.$watch(attrs.affixMe, function (val) {
        if (val) enable(); else disable();
      });
      scope.$on('$destroy', disable);
    }
  };
}
angular.module('algorea').directive('affixMe', affixMeDirective);

// Menus states
angular.module('algorea')
   .factory('layoutService', ['$rootScope', function ($rootScope) {
      function reset () {
        $rootScope.affix = 'toolbar';
        $rootScope.showNavTopOverlay = false;
      }
      $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
        reset();
      });
      reset();
      return {
         openNavTopOverlay: function () {
            $rootScope.showNavTopOverlay = true;
         },
         closeNavTopOverlay: function () {
            $rootScope.showNavTopOverlay = false;
         },
         openSidebarLeftOverlay: function () {
            $rootScope.showSidebarLeftOverlay = true;
         },
         closeSidebarLeftOverlay: function () {
            $rootScope.showSidebarLeftOverlay = false;
         },
         openMobileTopMenu: function () {
            $rootScope.mobileNavTopIsOpen = true;
         },
         closeMobileTopMenu: function () {
            $rootScope.mobileNavTopIsOpen = false;
         },
         openMobileTopMenuOverlay: function () {
            $rootScope.showMobileNavTopOverlay = true;
         },
         closeMobileTopMenuOverlay: function () {
            $rootScope.showMobileNavTopOverlay = false;
         },
         affixToolbar: function () {
            $rootScope.affix = 'toolbar';
         },
         affixHeader: function () {
            $rootScope.affix = 'header';
         }
      };
   }]);

angular.module('algorea')
  .controller('layoutController', ['$scope', '$window', '$timeout', '$rootScope', '$interval', '$injector', 'itemService', 'pathService', '$state', 'layoutService', function ($scope, $window, $timeout, $rootScope, $interval, $injector, itemService, pathService, $state, layoutService) {
    var pane_west = $('.ui-layout-west');
    var pane_center = $('.ui-layout-center');
    var container = $('#layoutContainer');
    var taskMinWidth = 820;
    var nonTaskMinWidth = 400;
    var mapService = null;
    if (config.domains.current.useMap) {
      mapService = $injector.get('mapService');
      mapService.setClickedCallback(function(path, lastItem) {
        if (lastItem.sType == 'Task' || lastItem.sType == 'Course') {
           var pathArray = path.split('/');
           var selr = pathArray.length;
           var sell = selr -1;
           var pathParams = pathService.getPathParams();
           if (pathParams.basePathStr == path) {
              $scope.layout.closeMap();
           } else {
              $state.go('contents', {path: path,sell:sell,selr:selr});
           }
        }
      });
    }
    $scope.mapInfos = {
       'mapPossible' : true,
       'hasMap':false,
       'mapOpened' : false
    };
   $scope.gotoIndex = function() {
      var defaultPathStr = config.domains.current.defaultPath;
      if (defaultPathStr.substr(0, 10) == '/contents/') {
         defaultPathStr = defaultPathStr.substr(10, defaultPathStr.length);
         var pathArray = defaultPathStr.split('/');
         var selr = pathArray.length;
         var sell = selr -1;
      }
      $state.go('contents', {path: defaultPathStr,sell:null,selr:null});
   }
    // $scope.layout will be accesset and set by viewButton directive in a subscope, so
    // it must be an object, or prototypal inheritance will mess everything
    $scope.layout = {
      west_is_open : true,
      tablesResized: function() {
      },
      refreshSizes : function() {

      },
      buttonClass: "fullscreen",
      state: "normal",
      navTopIsOpen: true,
      goFullscreen: function() {

      },
      goNormal: function() {

      },
      hasMap: function(hasMap, firstOpening) {
         $scope.mapInfos.hasMap = hasMap;
         if (!config.domains.current.useMap) return;
         if (hasMap == 'always') {
            $scope.layout.openMap();
         } else if (hasMap == 'never') {
            $scope.layout.closeMap();
         } else if (hasMap == 'button' && firstOpening) {
            $scope.layout.closeMap();
         }
      },
      openMap: function() {
         if (!$scope.mapInfos.mapMode && config.domains.current.useMap) {
            if ($scope.mapInfos.hasMap == 'button') {
               $scope.layout.navTopIsOpen = true;
            }
            $scope.mapInfos.mapMode = true;
            //$('#footer').hide();
            $('#view-right').hide();
            $('#map').show();
            mapService.show();
         }
      },
      closeMap: function() {
         if ($scope.mapInfos.mapMode) {
            if ($scope.mapInfos.hasMap == 'button') {
               $scope.layout.navTopIsOpen = false;
            }
            //$('#footer').show();
            $('#view-right').show();
            $('#map').hide();
            $scope.mapInfos.mapMode = false;
         }
      },
      openSidebarLeft: function() {
        $rootScope.sidebarLeftIsOpen = true;
        layoutService.openSidebarLeftOverlay(); // is hidden in CSS when not mobile layout to enable its visibility when resizing window
        $scope.layout.closeNavTopOverlay();
        if ($rootScope.isMobileLayout) {
          $scope.layout.closeMobileNavTop();
        }
      },
      closeSidebarLeft: function() {
        $rootScope.sidebarLeftIsOpen = false;
        layoutService.closeSidebarLeftOverlay();
      },
      closeSidebarLeftOverlay: function() {
        $scope.layout.closeSidebarLeft();
        layoutService.closeSidebarLeftOverlay();
      },
      openTaskMenu: function() {
         layoutService.affixHeader();
         layoutService.openNavTopOverlay();
         $scope.layout.navTopIsOpen = true;
      },
      openMobileTaskMenu: function() {
        layoutService.openNavTopOverlay();
        $scope.layout.navTopIsOpen = true;
        layoutService.openMobileTopMenu();
      },
      closeMobileTaskMenu: function() {
        layoutService.closeNavTopOverlay();
        $scope.layout.navTopIsOpen = false;
        layoutService.closeMobileTopMenu();
      },
      closeNavTopOverlay: function() {
        if ($rootScope.showNavTopOverlay) {
          layoutService.closeNavTopOverlay();
          layoutService.affixToolbar();
          $scope.layout.navTopIsOpen = false;
        }
      },
      openMobileNavTop: function() {
        layoutService.openMobileTopMenuOverlay();
        layoutService.openMobileTopMenu();
        if ($rootScope.isMobileLayout) {
          $scope.layout.closeSidebarLeft();
        }
      },
      closeMobileNavTop: function() {
        layoutService.closeMobileTopMenuOverlay();
        layoutService.closeMobileTopMenu();
      },
      breadcrumbsClicked: function(event) {
         // do not close menu when user clicks on link
         if (!config.domains.current.clickableMenu || event.target.parentNode.parentNode.className.indexOf('breadcrumbs-item') != -1 || event.target.className.indexOf('link-arrow') != -1) {
            return;
         }
      },
      toggleRight: function() {
         $('#sidebar-right').toggleClass('sidebar-right-toggled');
         $('#main-titlebar-community').toggleClass('main-titlebar-community-toggled');
         $scope.layout.refreshSizes();
      },
      setRightIcon: function() {
         if ($('#sidebar-right').hasClass('sidebar-right-toggled')) {
            $('#main-titlebar-community').addClass('main-titlebar-community-toggled');
         }
      },
      closeRight: function() {
         $scope.layout.rightOpen = false;
         if ($(window).width() < 1100) {
            if ($('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.toggleRight();
            }
         } else {
            if (!$('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.toggleRight();
            }
         }
      },
      openRight: function() {
         if ($(window).width() < 1100) {
            if (!$('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.rightOpen = true;
               $scope.layout.toggleRight();
            }
         } else {
            if ($('#sidebar-right').hasClass('sidebar-right-toggled')) {
               $scope.layout.rightOpen = true;
               $scope.layout.toggleRight();
            }
         }
      }
    };
   $scope.layout.rightIsFullScreen = function(rightIsFullScreen) {
      if (rightIsFullScreen == $rootScope.rightIsFullScreen) {
         return;
      }
      $rootScope.rightIsFullScreen = rightIsFullScreen;
       $scope.layout.navTopIsOpen = true;
       if (rightIsFullScreen) {
         if (!$scope.mapInfos.mapMode) {
           $scope.layout.navTopIsOpen = false;
         }
        $scope.layout.closeRight();
        $scope.layout.closeSidebarLeft();
       }
       $scope.layout.refreshSizes();
    };
    var isCurrentlyOnePage = !config.domains.current.useLeftNavigation;
    $scope.layout.isOnePage = function(isOnePage) {
       if (config.domains.current.useLeftNavigation) {
          if (typeof isOnePage === 'undefined') {
             return isCurrentlyOnePage || $rootScope.mapInfos.mapMode;
          }
          isCurrentlyOnePage = isOnePage;
       } else {
          return true;
       }
    };
    // inspired from https://github.com/capaj/ng-tools/blob/master/src/debounce.js
    // used on onresize for obvious performance reasons
    function debounce(fn, timeout, apply){
       apply = angular.isUndefined(apply) ? true : apply;
       var nthCall = 0;
       return function(){ // intercepting fn
          var that = this;
          var argz = arguments;
          nthCall++;
          var later = (function(version){
             return function(){
                if (version === nthCall){
                   return fn.apply(that, argz);
                }
             };
          })(nthCall);
          return $timeout(later, timeout, apply);
       };
    }
    $scope.layout.separateEditorOK = false;
    var lastSeparateEditorOK = false;
    $scope.layout.refreshSizes = function() {
       if ($rootScope.rightIsFullScreen) { // things are handled automatically for everything but the task layout
          var availableMainWidth = $('#main-area').width();
          var minWidth = $('#task-right').css('min-width');
          if (!minWidth) {minWidth = '0px';}
          minWidth = parseInt(minWidth.slice(0,-2));
          if (!minWidth) {minWidth = 800;}
          if (availableMainWidth - 2*minWidth > 40) {
            $scope.layout.separateEditorOK = true;
          } else {
            $scope.layout.separateEditorOK = false;
          }
         if (lastSeparateEditorOK != $scope.layout.separateEditorOK) {
            $timeout($rootScope.apply);
         }
         lastSeparateEditorOK = $scope.layout.separateEditorOK;
       } else {
         $scope.layout.separateEditorOK = false;
       }
    };

    var lastWindowWidth = $(window).width();
    $scope.layout.onResize = function () {
       // reset the opened/closed status of the left sidebar depending on the
       // new window width
       var newWindowWidth = $(window).width();
       lastWindowWidth = newWindowWidth;

       $scope.layout.refreshSizes();
       $rootScope.isMobileLayout = newWindowWidth < 700 ? true : false;
    }
    // resizing on window resizing (tamed)
    $window.onresize = debounce($scope.layout.onResize, 100, false);
    // function to be called at sync end by the service (it's alive. It's alive...)
    $rootScope.refreshSizes = $scope.layout.refreshSizes;
    // resizing on state change
    $rootScope.$on('$viewContentLoaded', function() {
      $rootScope.hasSidebarLeft = $state.current.views.left.template.length > 1 ? true : false;
      if ($rootScope.hasSidebarLeft && !$rootScope.isMobileLayout) {$scope.layout.openSidebarLeft();}
      $timeout($scope.layout.refreshSizes, 0); // 100 works here, might have to be changed for slow computers
    });
    $interval($scope.layout.refreshSizes, 1000);
    $scope.$on('layout.taskLayoutChange', $scope.layout.refreshSizes);
    $rootScope.sidebarLeftIsOpen = true;
    $rootScope.showSidebarLeftOverlay = false;
    $rootScope.mobileNavTopIsOpen = false;
    $rootScope.isMobileLayout = $(window).width() < 700 ? true : false;
}]);
<|endoftext|>


<commit_before>var Rx = require('rx');

var RR = {
  _observablePool: {},
  _push: function(arr, element) {
    return arr.push(element);
  },
  _replicate: function(source, subject) {
    return source.subscribe(
      function replicationOnNext(x) {
        subject.onNext(x);
      },
      function replicationOnError(err) {
        console.error(err);
      }
    );
  },
  _getObservable: function(dependence) {
    return RR._observablePool[dependence] = RR._observablePool[dependence] || new Rx.Subject();
  },
  _propertyOf: function(context) {
    return function(prop) { return context[prop]; };
  },
  _assignObj: function(obj) {
    return function(propVal, prop) { obj[prop] = propVal; };
  },
  _forOwn: function(subject, handler) {
    Object.keys(subject).map(function(prop, idx) {
      if (subject.hasOwnProperty(prop)) {
        handler(subject[prop], prop);
      }
    })
  },
  _extend: function(subject, extend) {
    RR._forOwn(extend || {}, RR._assignObj(subject));
    return subject;
  },
  observe: function(names) {
    return {
      componentDidMount: function() {
        names.forEach(function(name) {
          RR._replicate(this[name](), RR._getObservable(name));
        }.bind(this));
      }
    };
  },
  subscribe: function(store, opts) {
    return {
      componentWillMount: function() {
        RR._forOwn(opts, this._subscribeStore);
      },
      _subscribeStore: function(handlerName, prop) {
        store[prop].subscribe(this[handlerName]);
      }
    };
  },
  Observable: {
    create: function(dependencies, register, fetchFunc) {
      var observable = {};
      return RR._extend(observable, register.apply(observable, dependencies.map(fetchFunc || RR._propertyOf(RR._observablePool))));
    },
    createAction: function(dependencies, register) {
      return RR.Observable.create(dependencies, register, RR._getObservable);
    },
    createStore: function(action, dependencies, register) {
      return RR.Observable.create(dependencies, register, RR._propertyOf(action));
    }
  }
};

module.exports = RR;
<commit_msg>add bind observable<commit_after>var Rx = require('rx');

var RR = {
  _observablePool: {},
  _push: function(arr, element) {
    return arr.push(element);
  },
  _replicate: function(source, subject) {
    return source.subscribe(
      function replicationOnNext(x) {
        subject.onNext(x);
      },
      function replicationOnError(err) {
        console.error(err);
      }
    );
  },
  _getObservable: function(dependence) {
    return RR._observablePool[dependence] = RR._observablePool[dependence] || new Rx.Subject();
  },
  _propertyOf: function(context) {
    return function(prop) { return context[prop]; };
  },
  _assignObj: function(obj) {
    return function(propVal, prop) { obj[prop] = propVal; };
  },
  _forOwn: function(subject, handler) {
    Object.keys(subject).map(function(prop, idx) {
      if (subject.hasOwnProperty(prop)) {
        handler(subject[prop], prop);
      }
    })
  },
  _extend: function(subject, extend) {
    RR._forOwn(extend || {}, RR._assignObj(subject));
    return subject;
  },
  observe: function(names) {
    return {
      componentDidMount: function() {
        names.forEach(function(name) {
          RR._replicate(this[name](), RR._getObservable(name));
        }.bind(this));
      }
    };
  },
  subscribe: function(store, opts) {
    return {
      componentWillMount: function() {
        RR._forOwn(opts, this._subscribeStore);
      },
      _subscribeStore: function(handlerName, prop) {
        store[prop].subscribe(this[handlerName]);
      }
    };
  },
  Observable: {
    create: function(dependencies, register, fetchFunc) {
      var observable = {};
      return RR._extend(observable, register.apply(observable, dependencies.map(fetchFunc || RR._propertyOf(RR._observablePool))));
    },
    createAction: function(dependencies, register) {
      return RR.Observable.create(dependencies, register, RR._getObservable);
    },
    createStore: function(action, dependencies, register) {
      return RR.Observable.create(dependencies, register, RR._propertyOf(action));
    },
    bind: function(observableName) {
      var subject = new Rx.Subject();
      RR._replicate(subject, RR._getObservable(observableName));
      return function(evt) {
        return subject.onNext(evt);
      };
    }
  }
};

module.exports = RR;
<|endoftext|>


<commit_before>var mongodbClient = require('mongodb');

var mongoUri =  process.env.MONGOLAB_URI ||
                process.env.MONGOHQ_URL ||
                'mongodb://127.0.0.1:27017/Communique';
// var url = 'mongodb://127.0.0.1:27017/Communique';

var app = exports = module.exports = {};

app.init = function () {
    mongodbClient.connect(mongoUri, function (err, db) {
        // db.dropDatabase(function (err, result) {
            db.dropCollection('item', function (err, result) {
                db.createCollection('item', function (err, collection){
                    if (err) {
                        console.log('Create Error');
                    } else {
                        console.log('Create Successed');
                    }
                    db.close();
                });
            });
        // });
    });
};

app.insertData = function (data) {
    mongodbClient.connect(mongoUri, function (err, db) {
        db.collection('item', function (err, collection) {
            collection.insert(data, function (err, records) {
                if (err) {
                    console.log('Insert Error');
                } else {
                    // console.log("Insert Successed");
                    // console.log("Record added as", records[0]._id);
                }
                db.close();
            });
        });
    });
};

app.queryData = function (filter, getResult) {
    mongodbClient.connect(mongoUri, function (err, db) {
        db.collection('item', function (err, collection) {
            collection.find(filter, {'sort':[['date','desc']]}).toArray(function (err, docs) {
                db.close();
                getResult(docs);
            });
        });
    });
};
<commit_msg>remove collection data on heroku<commit_after>var mongodbClient = require('mongodb');

var mongoUri =  process.env.MONGOLAB_URI ||
                process.env.MONGOHQ_URL ||
                'mongodb://127.0.0.1:27017/Communique';
// var url = 'mongodb://127.0.0.1:27017/Communique';

var app = exports = module.exports = {};

app.init = function () {
    mongodbClient.connect(mongoUri, function (err, db) {
        // db.dropDatabase(function (err, result) {
            // db.dropCollection('item', function (err, result) {
            //     db.createCollection('item', function (err, collection){
            //         if (err) {
            //             console.log('Create Error');
            //         } else {
            //             console.log('Create Successed');
            //         }
            //         db.close();
            //     });
            // });
        // });
        db.collection('item', function (err, collection) {
            collection.remove();
            db.close();
        });
    });
};

app.insertData = function (data) {
    mongodbClient.connect(mongoUri, function (err, db) {
        db.collection('item', function (err, collection) {
            collection.insert(data, function (err, records) {
                if (err) {
                    console.log('Insert Error');
                } else {
                    // console.log("Insert Successed");
                    // console.log("Record added as", records[0]._id);
                }
                db.close();
            });
        });
    });
};

app.queryData = function (filter, getResult) {
    mongodbClient.connect(mongoUri, function (err, db) {
        db.collection('item', function (err, collection) {
            collection.find(filter, {'sort':[['date','desc']]}).toArray(function (err, docs) {
                db.close();
                getResult(docs);
            });
        });
    });
};
<|endoftext|>


<commit_before>"use strict";
require('dotenv').load();

const pmongo = require('promised-mongo');
const db = pmongo('mongodb://' + process.env.DBHOST + ':' + process.env.DBPORT + '/super-public', ['tasks', 'data']);

exports.add = function add(spData){
    return db.data.insert(spData);
};
<commit_msg>Support for db auth creds.<commit_after>"use strict";
require('dotenv').load();

const pmongo = require('promised-mongo');

var db;

if(process.env.DBUSER && process.env.DBPASS){
    db = pmongo('mongodb://' + process.env.DBUSER + ':' + process.env.DBPASS + '@' + process.env.DBHOST + ':' + process.env.DBPORT + '/super-public', ['tasks', 'data']);
} else {
    db = pmongo('mongodb://' + process.env.DBHOST + ':' + process.env.DBPORT + '/super-public', ['tasks', 'data']);
}

exports.add = function add(spData){
    return db.data.insert(spData);
};

<|endoftext|>


<commit_before>var mongo = require("mongodb");
var async = require("async");

exports.driver = db = null;

/*
 * Test and create db catalogs if not exists based on config object.
 */
exports.prepare = function( db_config, end_handler ){

	checkdb();

	var catalogs = db_config.system_catalogs.concat(db_config.user_catalogs);
	async.series(
	[
	function(next){//create system and user collections.
		async.forEach(catalogs,function(catalog_name,next){

			db.collection( catalog_name, function(err, collection){

				if( !collection ){

					db.createCollection( catalog_name, function(err, collection){

						if(err)
							throw err;
						else
							next();
					});
				}else
					next();
			});

		},function(err){

			next(err);
		});
	},
	function(next){//create journal index

		db.collection("events", function(err,collection){

			collection.ensureIndex("ev_data.wid",{unique:false},function(err){
				next(err);
			});
		});
	},
	function(next){//create users index.

		db.collection("users", function(err,collection){

			collection.ensureIndex("uid",{unique:false},function(err){
				next(err);
			});
		});
	}
	],function(err){

		end_handler(err, catalogs);
	});

}

/*
 * Close connection to db
 */
exports.close = function(){

	db && db.close();
	db = null;
}

/*
 * Connect to db before any other db operation.
 */
exports.connect = function( params, ret_handler ){

	if( db ){

		db.close();
	}

	params = params || {};
	params.dbhost = params.dbhost || "localhost";
	params.dbport = params.dbport || 27017;
	params.dbname = params.dbname || "jsa";

	exports.driver = db = new mongo.Db(params.dbname, new mongo.Server(params.dbhost,params.dbport,{auto_reconnect:true, socketOptions:{timeout:5000}}),{strict:true, safe:true}); //,poolSize:5,socketOptions:{timeout:1}

	db.open( function(err, client){

		if(client){
			if( params.dbuser !== undefined && params.dbpass !== undefined ){

				db.authenticate(params.dbuser,params.dbpass,function(err,client){

					if(client){
						console.log('**MongoDb driver auth-connected');
						if(ret_handler)
							ret_handler(null,1);
					}else{

						db = null;
						if(ret_handler)
							ret_handler(err,0);
					}
				});
			}else{
				if(ret_handler)
					ret_handler(null,1);
			}
		}else{

			db = null;
			if(ret_handler)
				ret_handler(err,0);
		}

	});

}

/*
 * Check db availability before any db operation.
 */
function checkdb(){

	if( !db )
		throw new Error("Driver not connected, should call connect first.");
}

/*
 * Save object to db.
 *
 */
var save = exports.save = function( col_name, obj, ret_handler ){

    checkdb();

	db.collection(col_name, function(err, collection){

	    collection.save( obj, {safe:true}, function(err, val){

			if( !err )
			    	ret_handler( null, val);
			else
				ret_handler(err,null);
	    });
	});

}

/*
 * Retrieve object from db by ID.
 *
 */
var select = exports.select = function( col_name, id_str, ret_handler ){

    checkdb();

	db.collection( col_name, function(err, collection){

		if(err){

			ret_handler(err,null);
			return;
		}

	    var ObjectID = require('mongodb').ObjectID;
	    collection.findOne({_id:new ObjectID(id_str)},{_noload:0},function(err, object){

			if( !err )
			    ret_handler( null,object );
			else
				ret_handler(err,null);
	    });
	});

}

/*
 * Funcion para seleccionar documentos de la bbdd.
 */
var criteria = exports.criteria = function( col_name, criteria, order_field, ret_handler ){

  	checkdb();

	db.collection( col_name, function(err, collection){

    	collection.find(criteria,{}).sort(order_field).toArray(function(err, objects){

			if(!err)
				ret_handler(null,objects);
			else
				ret_handler( err, null );
		});

	});
}

/*
 * Remove object from db by ID.
 */
var removeById = exports.removeById = function( col_name, id_str, ret_handler){

	checkdb();

	db.collection( col_name, function(err, collection){

		if(err){

			ret_handler(err,null);
			return;
		}

	    var ObjectID = require('mongodb').ObjectID;
	    collection.remove( {_id:new ObjectID(id_str)} ,{safe:true},function(err, n){

			if( ret_handler )
				if( !err )
				    ret_handler( null, n );
			    else
			    	ret_handler(err,null);
	    });
	});

}

/*
 * Remove object from db by criteria.
 */
var remove = exports.remove = function( col_name, criteria, ret_handler){

	checkdb();

	db.collection( col_name, function(err, collection){

		if(err){

			ret_handler(err,null);
			return;
		}

	    collection.remove( criteria ,{safe:true},function(err, n){//n: number of rows/docs removed

			if( ret_handler )
				if( !err )
				    ret_handler( null, n );
			    else
			    	ret_handler(err,null);
	    });
	});

}

/*
 * Remove expired document from db, its events and timer.
 */
var remove_global = exports.remove_global = function( col_name, id_str, ret_handler ){

	async.series([
		function(end_handler){

			var rcpts;
			async.series([
				function(next){

					select(col_name, id_str,function(err,doc){

						if(err)
							next(err);
						else{
							rcpts = doc.rcpts;
							next();
						}

					});
				},function(next){

					async.forEach(rcpts, function(rcpt,next){

						select("users",rcpt,function(err,user){//remove wid from all users wids list

							var index = user.wids.indexOf(id_str);
							if(index != -1){
								user.wids.splice(index,1);
								save("users",user,function(err){next(err);});
							}else
								next();
						});
					},function(err){

						next(err);
					});
				}
			],function(err){

				end_handler(err);
			});

		},
		function( end_handler ){//remove doc by id

			removeById(col_name, id_str, function(err){

				end_handler(err);
			});
		},
		function( end_handler ){//remove  events related to doc id

			remove("events",{"ev_data.wid":id_str}, function(err,n){

				end_handler(err);
			});
		}
	], function(err,results){

		if(err)
			throw err;
		else
			ret_handler(null,1);
	});

}

<commit_msg>1)New index creation at prepare method added: keywords array field at docs. 2){_noload:0} removed from find.<commit_after>var mongo = require("mongodb");
var async = require("async");

exports.driver = db = null;

/*
 * Test and create db catalogs if not exists based on config object.
 */
exports.prepare = function( db_config, end_handler ){

	checkdb();

	var catalogs = db_config.system_catalogs.concat(db_config.user_catalogs);
	async.series(
	[
	function(next){//create system and user collections.
		async.forEach(catalogs,function(catalog_name,next){

			db.collection( catalog_name, function(err, collection){

				if( !collection ){

					db.createCollection( catalog_name, function(err, collection){

						if(err)
							throw err;
						else
							next();
					});
				}else
					next();
			});

		},function(err){

			next(err);
		});
	},
	function(next){//create journal index

		db.collection("events", function(err,collection){

			collection.ensureIndex("ev_data.wid",{unique:false},function(err){
				next(err);
			});
		});
	},
	function(next){//create users index.

		db.collection("users", function(err,collection){

			collection.ensureIndex("uid",{unique:false},function(err){
				next(err);
			});
		});
	},
	function(next){//create docs keywords index

		db.collection(db_config.default_catalog,function(err,collection){

			collection.ensureIndex("keywords",{unique:false},function(err){
				next(err);
			});
		});
	}
	],function(err){

		end_handler(err, catalogs);
	});

}

/*
 * Close connection to db
 */
exports.close = function(){

	db && db.close();
	db = null;
}

/*
 * Connect to db before any other db operation.
 */
exports.connect = function( params, ret_handler ){

	if( db ){

		db.close();
	}

	params = params || {};
	params.dbhost = params.dbhost || "localhost";
	params.dbport = params.dbport || 27017;
	params.dbname = params.dbname || "jsa";

	exports.driver = db = new mongo.Db(params.dbname, new mongo.Server(params.dbhost,params.dbport,{auto_reconnect:true, socketOptions:{timeout:5000}}),{strict:true, safe:true}); //,poolSize:5,socketOptions:{timeout:1}

	db.open( function(err, client){

		if(client){
			if( params.dbuser !== undefined && params.dbpass !== undefined ){

				db.authenticate(params.dbuser,params.dbpass,function(err,client){

					if(client){
						console.log('**MongoDb driver auth-connected');
						if(ret_handler)
							ret_handler(null,1);
					}else{

						db = null;
						if(ret_handler)
							ret_handler(err,0);
					}
				});
			}else{
				if(ret_handler)
					ret_handler(null,1);
			}
		}else{

			db = null;
			if(ret_handler)
				ret_handler(err,0);
		}

	});

}

/*
 * Check db availability before any db operation.
 */
function checkdb(){

	if( !db )
		throw new Error("Driver not connected, should call connect first.");
}

/*
 * Save object to db.
 *
 */
var save = exports.save = function( col_name, obj, ret_handler ){

    checkdb();

	db.collection(col_name, function(err, collection){

	    collection.save( obj, {safe:true}, function(err, val){

			if( !err )
			    	ret_handler( null, val);
			else
				ret_handler(err,null);
	    });
	});

}

/*
 * Retrieve object from db by ID.
 *
 */
var select = exports.select = function( col_name, id_str, ret_handler ){

    checkdb();

	db.collection( col_name, function(err, collection){

		if(err){

			ret_handler(err,null);
			return;
		}

	    var ObjectID = require('mongodb').ObjectID;
	    collection.findOne({_id:new ObjectID(id_str)},function(err, object){

			if( !err )
			    ret_handler( null,object );
			else
				ret_handler(err,null);
	    });
	});

}

/*
 * Funcion para seleccionar documentos de la bbdd.
 */
var criteria = exports.criteria = function( col_name, criteria, order_field, ret_handler ){

  	checkdb();

	db.collection( col_name, function(err, collection){

    	collection.find(criteria,{}).sort(order_field).toArray(function(err, objects){

			if(!err)
				ret_handler(null,objects);
			else
				ret_handler( err, null );
		});

	});
}

/*
 * Remove object from db by ID.
 */
var removeById = exports.removeById = function( col_name, id_str, ret_handler){

	checkdb();

	db.collection( col_name, function(err, collection){

		if(err){

			ret_handler(err,null);
			return;
		}

	    var ObjectID = require('mongodb').ObjectID;
	    collection.remove( {_id:new ObjectID(id_str)} ,{safe:true},function(err, n){

			if( ret_handler )
				if( !err )
				    ret_handler( null, n );
			    else
			    	ret_handler(err,null);
	    });
	});

}

/*
 * Remove object from db by criteria.
 */
var remove = exports.remove = function( col_name, criteria, ret_handler){

	checkdb();

	db.collection( col_name, function(err, collection){

		if(err){

			ret_handler(err,null);
			return;
		}

	    collection.remove( criteria ,{safe:true},function(err, n){//n: number of rows/docs removed

			if( ret_handler )
				if( !err )
				    ret_handler( null, n );
			    else
			    	ret_handler(err,null);
	    });
	});

}

/*
 * Remove expired document from db, its events and timer.
 */
var remove_global = exports.remove_global = function( col_name, id_str, ret_handler ){

	async.series([
		function(end_handler){

			var rcpts;
			async.series([
				function(next){

					select(col_name, id_str,function(err,doc){

						if(err)
							next(err);
						else{
							rcpts = doc.rcpts;
							next();
						}

					});
				},function(next){

					async.forEach(rcpts, function(rcpt,next){

						select("users",rcpt,function(err,user){//remove wid from all users wids list

							var index = user.wids.indexOf(id_str);
							if(index != -1){
								user.wids.splice(index,1);
								save("users",user,function(err){next(err);});
							}else
								next();
						});
					},function(err){

						next(err);
					});
				}
			],function(err){

				end_handler(err);
			});

		},
		function( end_handler ){//remove doc by id

			removeById(col_name, id_str, function(err){

				end_handler(err);
			});
		},
		function( end_handler ){//remove  events related to doc id

			remove("events",{"ev_data.wid":id_str}, function(err,n){

				end_handler(err);
			});
		}
	], function(err,results){

		if(err)
			throw err;
		else
			ret_handler(null,1);
	});

}

<|endoftext|>