<commit_before>'use strict';

var consts = require('./constants');
var es = require('event-stream');
var sgvdata = require('sgvdata');

/*
 * API - Expose Nightscout HTTP API
 * This api is designed to work with express.
 */
function api (env, entries, settings) {

    // our globals
    var express = require('express'),
        api = express()
    ;

    // some middleware
    var verifyAuthorization = require('./middleware/verify-token')(env),
        sendJSONStatus = require('./middleware/send-json-status')( ),
        bodyParser = require('body-parser')
        ;

    // set up express basics
    api.set('title', 'Nightscout API v1');

    // invoke common middleware
    api.use(sendJSONStatus);
    // text body types get handled as raw buffer stream
    api.use(bodyParser.raw());
    // json body types get handled as parsed json
    api.use(bodyParser.json());
    // shortcut to use extension to specify output content-type
    api.use(require('express-extension-to-accept')([
      'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv'
    ]));

    // also support url-encoded content-type
    api.use(bodyParser.urlencoded({
        extended: true
    }));

    /*
     * Start setting up routes
     */

    // Some experiments
    api.get('/authorized/:secret/test', verifyAuthorization, function (req, res, next) {
        return res.json({status: 'ok'});
    });

    api.get('/authorized/test', verifyAuthorization, function (req, res, next) {
        return res.json({status: 'ok'});
    });

    // Status badge/text/json
    api.get('/status', function (req, res, next) {
        var status = {status: 'ok'};
        var badge = 'http://img.shields.io/badge/Nightscout-OK-green';
        return res.format({
          html: function ( ) {
            res.send("
STATUS OK
");
          },
          png: function ( ) {
            res.redirect(302, badge + '.png');
          },
          svg: function ( ) {
            res.redirect(302, badge + '.svg');
          },
          text: function ( ) {
            res.send("STATUS OK");
          },
          json: function ( ) {
            res.json(status);
          }
        });
    });

    /**********\
     * Entries
    \**********/

    // Middleware to format any response involving entries.
    function format_entries (req, res, next) {
      var output = es.readArray(res.entries || [ ]);
      if (res.entries_err) return res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
      return res.format({
        text: function ( ) {
          es.pipeline(output, sgvdata.format( ), res);
        },
        json: function ( ) {
          es.pipeline(output, sgvdata.lint({strict: false}), es.writeArray(function (err, out) {
            res.json(out);
          }));
        }
      });
    }

    // middleware to process "uploads" of sgv data
    function insert_entries (req, res, next) {
      // list of incoming records
      var incoming = [ ];
      // Potentially a single json encoded body.
      // This can happen from either an url-encoded or json content-type.
      if ('sgv' in req.body) {
        // add it to the incoming list
        incoming.push(req.body);
      }
      // potentially a list of json entries
      if (req.body.length) {
        // add them to the list
        incoming = incoming.concat(req.body);
      }

      /*
       * inputs -> <ReadableStream>
       * in node, pipe is the most interoperable interface
       * inputs returns a readable stream representing all the potential
       * records from the HTTP body.
       * Most content-types are handled by express middeware.
       * However, text/* types are given to us as a raw buffer, this
       * function switches between these two variants to find the
       * correct input stream.
       * stream, so use svgdata to handle those.
       * The inputs stream always emits sgv json objects.
       */
      function inputs ( ) {
        var input;
        // handle all text types
        if (req.is('text/*')) {
          // re-use the svgdata parsing stream
          input = es.pipeline(req, sgvdata.parse( ));
          return input;
        }
        // use established list
        return es.readArray(incoming);
      }

      // return a writable persistent storage stream
      function persist (fn) {
        if (req.persist_entries) {
          // store everything
          return entries.persist(fn);
        }
        // support a preview mode, just lint everything
        return es.pipeline(entries.map( ), es.writeArray(fn));
      }

      // store results and move to the next middleware
      function done (err, result) {
        res.entries = result;
        res.entries_err = err;
        return next( );
      }

      // pipe everything to persistent storage
      // when finished, pass to the next piece of middleware
      es.pipeline(inputs( ), persist(done));
    }

    api.get('/entries', function(req, res, next) {
      // If "?count=" is present, use that number to decided how many to return.
      var query = req.query;
      entries.list(query, function(err, entries) {
        res.entries = entries;
        res.entries_err = err;
        return next( );
      });
      return;
    }, format_entries);

    api.get('/entries/current', function(req, res, next) {
      entries.list({count: 1}, function(err, entries) {
        res.entries = entries;
        res.entries_err = err;
        return next( );
      });
      return;
    }, format_entries);

    // Allow previewing your post content, just echos everything you
    // posted back out.
    api.post('/entries/preview', function (req, res, next) {
      req.persist_entries = false;
      next( );
      return;
    }, insert_entries, format_entries);

    // Create and store new sgv entries
    api.post('/entries', verifyAuthorization, function (req, res, next) {
      req.persist_entries = true;
      next( );
      return;
    }, insert_entries, format_entries);

    // Fetch one entry by id
    api.get('/entries/:id', function(req, res) {
        entries.getEntry(function(err, entry) {
            if (err)
                res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
            else
                return res.json(entry);
        }, req.params.id);
    });

    // Fetch settings
    api.get('/settings', function(req, res) {
        settings.getSettings(function(err, settings) {
            if (err)
                res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
            else
                return res.json(settings);
        });
    });

    // Delete settings
    api.delete('/settings', verifyAuthorization, function(req, res) {
      settings.remove(function ( ) {
        res.json({ });
      });
    });

    // Replace settings
    api.put('/settings', verifyAuthorization, function(req, res) {
        // Retrieve the JSON formatted record.
        var json = req.body;
        console.log(req.body, json);

        // Send the new settings to mongodb.
        settings.updateSettings(json, function(err, config) {
            if (err)
                res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
            else {
                // Add a warning to the outgoing status when HTTPS is not being used.
                var warning = '';
                if (req.secure === false)
                    warning = 'WARNING: HTTPS is required to secure your data!';

                res.json(config);
            }
        });
    });

    return api;
}
module.exports = api;

<commit_msg>introduce express.Router( ), small renaming<commit_after>'use strict';

var consts = require('./constants');
var es = require('event-stream');
var sgvdata = require('sgvdata');

/*
 * API - Expose Nightscout HTTP API
 * This api is designed to work with express.
 */
function configure (env, entries, settings) {

    // our globals
    var express = require('express'),
        app = express( ),
        api = express.Router()
    ;

    // some middleware
    var verifyAuthorization = require('./middleware/verify-token')(env),
        sendJSONStatus = require('./middleware/send-json-status')( ),
        bodyParser = require('body-parser')
        ;

    // set up express basics
    app.set('title', 'Nightscout API v1');

    // invoke common middleware
    api.use(sendJSONStatus);
    // text body types get handled as raw buffer stream
    api.use(bodyParser.raw());
    // json body types get handled as parsed json
    api.use(bodyParser.json());
    // shortcut to use extension to specify output content-type
    api.use(require('express-extension-to-accept')([
      'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv'
    ]));

    // also support url-encoded content-type
    api.use(bodyParser.urlencoded({
        extended: true
    }));

    /*
     * Start setting up routes
     */

    // Some experiments
    api.get('/authorized/:secret/test', verifyAuthorization, function (req, res, next) {
        return res.json({status: 'ok'});
    });

    api.get('/authorized/test', verifyAuthorization, function (req, res, next) {
        return res.json({status: 'ok'});
    });

    // Status badge/text/json
    api.get('/status', function (req, res, next) {
        var status = {status: 'ok'};
        var badge = 'http://img.shields.io/badge/Nightscout-OK-green';
        return res.format({
          html: function ( ) {
            res.send("
STATUS OK
");
          },
          png: function ( ) {
            res.redirect(302, badge + '.png');
          },
          svg: function ( ) {
            res.redirect(302, badge + '.svg');
          },
          text: function ( ) {
            res.send("STATUS OK");
          },
          json: function ( ) {
            res.json(status);
          }
        });
    });

    /**********\
     * Entries
    \**********/

    // Middleware to format any response involving entries.
    function format_entries (req, res, next) {
      var output = es.readArray(res.entries || [ ]);
      if (res.entries_err) return res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
      return res.format({
        text: function ( ) {
          es.pipeline(output, sgvdata.format( ), res);
        },
        json: function ( ) {
          es.pipeline(output, sgvdata.lint({strict: false}), es.writeArray(function (err, out) {
            res.json(out);
          }));
        }
      });
    }

    // middleware to process "uploads" of sgv data
    function insert_entries (req, res, next) {
      // list of incoming records
      var incoming = [ ];
      // Potentially a single json encoded body.
      // This can happen from either an url-encoded or json content-type.
      if ('sgv' in req.body) {
        // add it to the incoming list
        incoming.push(req.body);
      }
      // potentially a list of json entries
      if (req.body.length) {
        // add them to the list
        incoming = incoming.concat(req.body);
      }

      /*
       * inputs -> <ReadableStream>
       * in node, pipe is the most interoperable interface
       * inputs returns a readable stream representing all the potential
       * records from the HTTP body.
       * Most content-types are handled by express middeware.
       * However, text/* types are given to us as a raw buffer, this
       * function switches between these two variants to find the
       * correct input stream.
       * stream, so use svgdata to handle those.
       * The inputs stream always emits sgv json objects.
       */
      function inputs ( ) {
        var input;
        // handle all text types
        if (req.is('text/*')) {
          // re-use the svgdata parsing stream
          input = es.pipeline(req, sgvdata.parse( ));
          return input;
        }
        // use established list
        return es.readArray(incoming);
      }

      // return a writable persistent storage stream
      function persist (fn) {
        if (req.persist_entries) {
          // store everything
          return entries.persist(fn);
        }
        // support a preview mode, just lint everything
        return es.pipeline(entries.map( ), es.writeArray(fn));
      }

      // store results and move to the next middleware
      function done (err, result) {
        res.entries = result;
        res.entries_err = err;
        return next( );
      }

      // pipe everything to persistent storage
      // when finished, pass to the next piece of middleware
      es.pipeline(inputs( ), persist(done));
    }

    api.get('/entries', function(req, res, next) {
      // If "?count=" is present, use that number to decided how many to return.
      var query = req.query;
      entries.list(query, function(err, entries) {
        res.entries = entries;
        res.entries_err = err;
        return next( );
      });
      return;
    }, format_entries);

    api.get('/entries/current', function(req, res, next) {
      entries.list({count: 1}, function(err, entries) {
        res.entries = entries;
        res.entries_err = err;
        return next( );
      });
      return;
    }, format_entries);

    // Allow previewing your post content, just echos everything you
    // posted back out.
    api.post('/entries/preview', function (req, res, next) {
      req.persist_entries = false;
      next( );
      return;
    }, insert_entries, format_entries);

    // Create and store new sgv entries
    api.post('/entries', verifyAuthorization, function (req, res, next) {
      req.persist_entries = true;
      next( );
      return;
    }, insert_entries, format_entries);

    // Fetch one entry by id
    api.get('/entries/:id', function(req, res) {
        entries.getEntry(function(err, entry) {
            if (err)
                res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
            else
                return res.json(entry);
        }, req.params.id);
    });

    // Fetch settings
    api.get('/settings', function(req, res) {
        settings.getSettings(function(err, settings) {
            if (err)
                res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
            else
                return res.json(settings);
        });
    });

    // Delete settings
    api.delete('/settings', verifyAuthorization, function(req, res) {
      settings.remove(function ( ) {
        res.json({ });
      });
    });

    // Replace settings
    api.put('/settings', verifyAuthorization, function(req, res) {
        // Retrieve the JSON formatted record.
        var json = req.body;
        console.log(req.body, json);

        // Send the new settings to mongodb.
        settings.updateSettings(json, function(err, config) {
            if (err)
                res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
            else {
                // Add a warning to the outgoing status when HTTPS is not being used.
                var warning = '';
                if (req.secure === false)
                    warning = 'WARNING: HTTPS is required to secure your data!';

                res.json(config);
            }
        });
    });

    function debug ( ) {
      console.log('ROUTES', this.stack);
      for (var k in this) {
        console.log('KEY', k);
      }
      console.log(this.settings);
      console.log(this.locals);
      console.log("FOOBAR", this.get && this.get('foobar'))
    }
    app.set('foobar', "MY VALUE");

    debug.call(api);
    app.use(api);
    debug.call(app);

    return app;
}
module.exports = configure;

<|endoftext|>


<commit_before>var util = require( "util" ),
    assert = require( "assert" );

var undefined,
    noop = function(){},
    pause = false,
    queue = [],
    test,
    module = {},
    QUnit = {
        api: {}
    };

exports.QUnit = QUnit;

/**
 * Setup the env, run the test and then run the next one
 */
function run() {
    // all tests are done
    if ( queue.length <= 0 ) {
        QUnit.done();
        return;
    }

    test = queue[0];
    module = test.module;

    if ( module.env && module.env.setup && !test.setupDone ) {
        module.env.setup();
        test.setupDone = true;
    }

    // run the test
    if ( !pause ) {
        test.fn.call( QUnit.current_testEnvironment = extend( true, test.env || {}, module.env ) );
    }

    if ( test.async ) {
        QUnit.api.stop();
    } else if ( !pause ) {
        testDone();
    }

    if ( !pause ) {
        run();
    }
}

/**
 * Do teardown and count expected assertions after each test is done
 */
function testDone() {

    if ( !test || !module ) {
        return;
    }

    if ( module.env && module.env.teardown && !test.teardownDone ) {
        module.env.teardown();
        test.teardownDone = true;
    }

    if ( pause ) {
        return;
    }

    // check if expected assertions count is correct
    if ( test.expected !== undefined && test.expected !== test.done ) {
        var errorStack;
        // trigger an error to get an error stack
        try {
            throw new Error( "Assertions amount" );
        } catch( err ) {
            errorStack = err.stack;
        }

        log({
            message: "Expected " + test.expected + " assertions, but " + test.done + " were run",
            errorStack: errorStack
        });
    }

    queue.shift();
}

/**
 * Print stringified object to stdout
 * @param {Object} obj
 */
function log( obj ) {

    obj.message = obj.message || "";
    obj.name = test.name || "";
    obj.module = module.name || "";

    util.print( JSON.stringify(obj) + "\n" );
}

/**
 * Ecma 5 doesn't defines this method, so just hope they also like jQuery's way :)
 * You can pass so much objects as you like, all properties will be copied to the first object
 *
 * @param {Object|Boolean} deep
 * @param {Object}
 * @return {Object}
 */
function extend( deep /*, obj, obj, ...*/ ) {
    // take first argument, if its not a boolean
    var args = arguments,
        i = typeof deep === 'boolean' ? 1 : 0,
        target = args[i];

    for ( ; i < args.length; ++i ) {
        typeof args[i] === "object" && Object.keys( args[i] ).forEach(function( key ) {
            // if deep extending and both of keys are objects
            if (key == "console") {
              return;
            }
            if ( deep === true && target[key] ) {
                args.callee(deep, target[key], args[i][key]);
            } else {
                target[key] = args[i][key];
            }
        });
    }

    return target;
}

exports.extend = extend;

/**
 * Begin tests execution
 */
QUnit.begin = function() {
    if ( queue.length <= 0 ) {
        throw new Error( "There are no tests defined" );
    }

    this.start();
};

/**
 * Callback for tests complete
 */
QUnit.done = noop;

/**
 * Define current module name and environment
 * @param {String} name
 * @param {Object} env
 */
QUnit.api.module = function( name, env ) {

    env = env || {};

    module = {
        name: name,
        env: env
    };

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 * @param {Boolean} async add test as async one
 */
QUnit.api.test = function( name, expect, fn, async ) {

    if ( typeof name !== "string" ) {
        throw new Error( "Test name should be the first parameter" );
    }

    var type = typeof expect,
        env;
    if ( type === "function" ) {
        fn = expect;
        expect = undefined;
    // expect can extend module env
    } else if ( type === "object" ) {
        env = expect;
        expect = undefined;
    }

    if ( typeof fn !== "function" ) {
        throw new Error( "No test function passed" );
    }

    queue.push({
        name: name,
        module: module,
        fn: fn,
        expected: expect,
        env: env,
        done: 0,
        async: async
    });

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 */
QUnit.api.asyncTest = function( name, expect, fn ) {
    this.test( name, expect, fn, true );
    return this;
};

/**
 * Start tests execution
 */
QUnit.api.start = function() {
    pause = false;
    testDone();
    run();
    return this;
};

/**
 * Stop tests execution
 */
QUnit.api.stop = function() {
    pause = true;
    return this;
};

/**
 * Set expected amount of assertions for current test
 * @param {Number} amount
 */
QUnit.api.expect = function( amount ) {
    test.expected = amount;
    return this;
};

/**
 * A boolean assertion. Passes if the first argument is truthy.
 * @param {Boolean} value
 * @param {String} message
 */
QUnit.api.ok = function( value, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.ok( value );
    } catch (err) {
        r.errorStack = err.stack;
    }

    log(r);

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.equals = QUnit.api.equal = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.equal( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.notEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.notEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ===.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.strictEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.strictEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A deep recursive comparison assertion, working on primitive types, arrays and objects.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.same = QUnit.api.deepEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.deepEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

QUnit.api.raises = function( actual, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.throws( actual, message );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * Original function from QUnit
 * @param {Object} obj
 * @return {String|undefined}
 */
QUnit.objectType = function( obj ) {
    if (typeof obj === "undefined") {
        return "undefined";
    }
    // consider: typeof null === object
    if (obj === null) {
        return "null";
    }

    var type = Object.prototype.toString.call( obj )
        .match(/^\[object\s(.*)\]$/)[1] || '';

    switch (type) {
        case 'Number':
        if (isNaN(obj)) {
            return "nan";
        } else {
            return "number";
        }
        case 'String':
        case 'Boolean':
        case 'Array':
        case 'Date':
        case 'RegExp':
        case 'Function':
            return type.toLowerCase();
    }
    if (typeof obj === "object") {
        return "object";
    }
    return undefined;
}

// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {

    var innerEquiv; // the real equiv function
    var callers = []; // stack to decide between skip/abort functions
    var parents = []; // stack to avoiding loops from circular referencing

    // Call the o related callback with the given arguments.
    function bindCallbacks(o, callbacks, args) {
        var prop = QUnit.objectType(o);
        if (prop) {
            if (QUnit.objectType(callbacks[prop]) === "function") {
                return callbacks[prop].apply(callbacks, args);
            } else {
                return callbacks[prop]; // or undefined
            }
        }
    }

    var callbacks = function () {

        // for string, boolean, number and null
        function useStrictEquality(b, a) {
            if (b instanceof a.constructor || a instanceof b.constructor) {
                // to catch short annotaion VS 'new' annotation of a declaration
                // e.g. var i = 1;
                // var j = new Number(1);
                return a == b;
            } else {
                return a === b;
            }
        }

        return {
            "string": useStrictEquality,
            "boolean": useStrictEquality,
            "number": useStrictEquality,
            "null": useStrictEquality,
            "undefined": useStrictEquality,

            "nan": function (b) {
                return isNaN(b);
            },

            "date": function (b, a) {
                return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
            },

            "regexp": function (b, a) {
                return QUnit.objectType(b) === "regexp" &&
                    a.source === b.source && // the regex itself
                    a.global === b.global && // and its modifers (gmi) ...
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline;
            },

            // - skip when the property is a method of an instance (OOP)
            // - abort otherwise,
            // initial === would have catch identical references anyway
            "function": function () {
                var caller = callers[callers.length - 1];
                return caller !== Object &&
                        typeof caller !== "undefined";
            },

            "array": function (b, a) {
                var i, j, loop;
                var len;

                // b could be an object literal here
                if ( ! (QUnit.objectType(b) === "array")) {
                    return false;
                }

                len = a.length;
                if (len !== b.length) { // safe and faster
                    return false;
                }

                //track reference to avoid circular references
                parents.push(a);
                for (i = 0; i < len; i++) {
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i]){
                            loop = true;//dont rewalk array
                        }
                    }
                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        parents.pop();
                        return false;
                    }
                }
                parents.pop();
                return true;
            },

            "object": function (b, a) {
                var i, j, loop;
                var eq = true; // unless we can proove it
                var aProperties = [], bProperties = []; // collection of strings

                // comparing constructors is more strict than using instanceof
                if ( a.constructor !== b.constructor) {
                    return false;
                }

                // stack constructor before traversing properties
                callers.push(a.constructor);
                //track reference to avoid circular references
                parents.push(a);

                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i])
                            loop = true; //don't go down the same path twice
                    }
                    aProperties.push(i); // collect a's properties

                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        eq = false;
                        break;
                    }
                }

                callers.pop(); // unstack, we are done
                parents.pop();

                for (i in b) {
                    bProperties.push(i); // collect b's properties
                }

                // Ensures identical properties name
                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
            }
        };
    }();

    innerEquiv = function () { // can take multiple arguments
        var args = Array.prototype.slice.apply(arguments);
        if (args.length < 2) {
            return true; // end transition
        }

        return (function (a, b) {
            if (a === b) {
                return true; // catch the most you can
            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
                return false; // don't lose time with error prone cases
            } else {
                return bindCallbacks(a, callbacks, [b, a]);
            }

        // apply transition with (1..n) arguments
        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
    };

    return innerEquiv;

}();

// copy api to QUnit namespace
extend( QUnit, QUnit.api );

global.QUnit = QUnit;

// make qunit api global
extend( global, QUnit.api );
<commit_msg>Still busted on Ubuntu - try writing the whole buffer at once<commit_after>var util = require( "util" ),
    assert = require( "assert" );

var undefined,
    noop = function(){},
    pause = false,
    queue = [],
    results = [],
    test,
    module = {},
    QUnit = {
        api: {}
    };

exports.QUnit = QUnit;

/**
 * Setup the env, run the test and then run the next one
 */
function run() {
    // all tests are done
    if ( queue.length <= 0 ) {
        util.print( JSON.stringify(results) );
        QUnit.done();
        return;
    }

    test = queue[0];
    module = test.module;

    if ( module.env && module.env.setup && !test.setupDone ) {
        module.env.setup();
        test.setupDone = true;
    }

    // run the test
    if ( !pause ) {
        test.fn.call( QUnit.current_testEnvironment = extend( true, test.env || {}, module.env ) );
    }

    if ( test.async ) {
        QUnit.api.stop();
    } else if ( !pause ) {
        testDone();
    }

    if ( !pause ) {
        run();
    }
}

/**
 * Do teardown and count expected assertions after each test is done
 */
function testDone() {

    if ( !test || !module ) {
        return;
    }

    if ( module.env && module.env.teardown && !test.teardownDone ) {
        module.env.teardown();
        test.teardownDone = true;
    }

    if ( pause ) {
        return;
    }

    // check if expected assertions count is correct
    if ( test.expected !== undefined && test.expected !== test.done ) {
        var errorStack;
        // trigger an error to get an error stack
        try {
            throw new Error( "Assertions amount" );
        } catch( err ) {
            errorStack = err.stack;
        }

        log({
            message: "Expected " + test.expected + " assertions, but " + test.done + " were run",
            errorStack: errorStack
        });
    }

    queue.shift();
}

/**
 * Print stringified object to stdout
 * @param {Object} obj
 */
function log( obj ) {

    obj.message = obj.message || "";
    obj.name = test.name || "";
    obj.module = module.name || "";

    results.push(obj);
}

/**
 * Ecma 5 doesn't defines this method, so just hope they also like jQuery's way :)
 * You can pass so much objects as you like, all properties will be copied to the first object
 *
 * @param {Object|Boolean} deep
 * @param {Object}
 * @return {Object}
 */
function extend( deep /*, obj, obj, ...*/ ) {
    // take first argument, if its not a boolean
    var args = arguments,
        i = typeof deep === 'boolean' ? 1 : 0,
        target = args[i];

    for ( ; i < args.length; ++i ) {
        typeof args[i] === "object" && Object.keys( args[i] ).forEach(function( key ) {
            // if deep extending and both of keys are objects
            if (key == "console") {
              return;
            }
            if ( deep === true && target[key] ) {
                args.callee(deep, target[key], args[i][key]);
            } else {
                target[key] = args[i][key];
            }
        });
    }

    return target;
}

exports.extend = extend;

/**
 * Begin tests execution
 */
QUnit.begin = function() {
    if ( queue.length <= 0 ) {
        throw new Error( "There are no tests defined" );
    }

    this.start();
};

/**
 * Callback for tests complete
 */
QUnit.done = noop;

/**
 * Define current module name and environment
 * @param {String} name
 * @param {Object} env
 */
QUnit.api.module = function( name, env ) {

    env = env || {};

    module = {
        name: name,
        env: env
    };

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 * @param {Boolean} async add test as async one
 */
QUnit.api.test = function( name, expect, fn, async ) {

    if ( typeof name !== "string" ) {
        throw new Error( "Test name should be the first parameter" );
    }

    var type = typeof expect,
        env;
    if ( type === "function" ) {
        fn = expect;
        expect = undefined;
    // expect can extend module env
    } else if ( type === "object" ) {
        env = expect;
        expect = undefined;
    }

    if ( typeof fn !== "function" ) {
        throw new Error( "No test function passed" );
    }

    queue.push({
        name: name,
        module: module,
        fn: fn,
        expected: expect,
        env: env,
        done: 0,
        async: async
    });

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 */
QUnit.api.asyncTest = function( name, expect, fn ) {
    this.test( name, expect, fn, true );
    return this;
};

/**
 * Start tests execution
 */
QUnit.api.start = function() {
    pause = false;
    testDone();
    run();
    return this;
};

/**
 * Stop tests execution
 */
QUnit.api.stop = function() {
    pause = true;
    return this;
};

/**
 * Set expected amount of assertions for current test
 * @param {Number} amount
 */
QUnit.api.expect = function( amount ) {
    test.expected = amount;
    return this;
};

/**
 * A boolean assertion. Passes if the first argument is truthy.
 * @param {Boolean} value
 * @param {String} message
 */
QUnit.api.ok = function( value, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.ok( value );
    } catch (err) {
        r.errorStack = err.stack;
    }

    log(r);

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.equals = QUnit.api.equal = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.equal( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.notEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.notEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ===.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.strictEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.strictEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A deep recursive comparison assertion, working on primitive types, arrays and objects.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.same = QUnit.api.deepEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.deepEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

QUnit.api.raises = function( actual, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.throws( actual, message );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * Original function from QUnit
 * @param {Object} obj
 * @return {String|undefined}
 */
QUnit.objectType = function( obj ) {
    if (typeof obj === "undefined") {
        return "undefined";
    }
    // consider: typeof null === object
    if (obj === null) {
        return "null";
    }

    var type = Object.prototype.toString.call( obj )
        .match(/^\[object\s(.*)\]$/)[1] || '';

    switch (type) {
        case 'Number':
        if (isNaN(obj)) {
            return "nan";
        } else {
            return "number";
        }
        case 'String':
        case 'Boolean':
        case 'Array':
        case 'Date':
        case 'RegExp':
        case 'Function':
            return type.toLowerCase();
    }
    if (typeof obj === "object") {
        return "object";
    }
    return undefined;
}

// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {

    var innerEquiv; // the real equiv function
    var callers = []; // stack to decide between skip/abort functions
    var parents = []; // stack to avoiding loops from circular referencing

    // Call the o related callback with the given arguments.
    function bindCallbacks(o, callbacks, args) {
        var prop = QUnit.objectType(o);
        if (prop) {
            if (QUnit.objectType(callbacks[prop]) === "function") {
                return callbacks[prop].apply(callbacks, args);
            } else {
                return callbacks[prop]; // or undefined
            }
        }
    }

    var callbacks = function () {

        // for string, boolean, number and null
        function useStrictEquality(b, a) {
            if (b instanceof a.constructor || a instanceof b.constructor) {
                // to catch short annotaion VS 'new' annotation of a declaration
                // e.g. var i = 1;
                // var j = new Number(1);
                return a == b;
            } else {
                return a === b;
            }
        }

        return {
            "string": useStrictEquality,
            "boolean": useStrictEquality,
            "number": useStrictEquality,
            "null": useStrictEquality,
            "undefined": useStrictEquality,

            "nan": function (b) {
                return isNaN(b);
            },

            "date": function (b, a) {
                return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
            },

            "regexp": function (b, a) {
                return QUnit.objectType(b) === "regexp" &&
                    a.source === b.source && // the regex itself
                    a.global === b.global && // and its modifers (gmi) ...
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline;
            },

            // - skip when the property is a method of an instance (OOP)
            // - abort otherwise,
            // initial === would have catch identical references anyway
            "function": function () {
                var caller = callers[callers.length - 1];
                return caller !== Object &&
                        typeof caller !== "undefined";
            },

            "array": function (b, a) {
                var i, j, loop;
                var len;

                // b could be an object literal here
                if ( ! (QUnit.objectType(b) === "array")) {
                    return false;
                }

                len = a.length;
                if (len !== b.length) { // safe and faster
                    return false;
                }

                //track reference to avoid circular references
                parents.push(a);
                for (i = 0; i < len; i++) {
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i]){
                            loop = true;//dont rewalk array
                        }
                    }
                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        parents.pop();
                        return false;
                    }
                }
                parents.pop();
                return true;
            },

            "object": function (b, a) {
                var i, j, loop;
                var eq = true; // unless we can proove it
                var aProperties = [], bProperties = []; // collection of strings

                // comparing constructors is more strict than using instanceof
                if ( a.constructor !== b.constructor) {
                    return false;
                }

                // stack constructor before traversing properties
                callers.push(a.constructor);
                //track reference to avoid circular references
                parents.push(a);

                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i])
                            loop = true; //don't go down the same path twice
                    }
                    aProperties.push(i); // collect a's properties

                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        eq = false;
                        break;
                    }
                }

                callers.pop(); // unstack, we are done
                parents.pop();

                for (i in b) {
                    bProperties.push(i); // collect b's properties
                }

                // Ensures identical properties name
                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
            }
        };
    }();

    innerEquiv = function () { // can take multiple arguments
        var args = Array.prototype.slice.apply(arguments);
        if (args.length < 2) {
            return true; // end transition
        }

        return (function (a, b) {
            if (a === b) {
                return true; // catch the most you can
            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
                return false; // don't lose time with error prone cases
            } else {
                return bindCallbacks(a, callbacks, [b, a]);
            }

        // apply transition with (1..n) arguments
        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
    };

    return innerEquiv;

}();

// copy api to QUnit namespace
extend( QUnit, QUnit.api );

global.QUnit = QUnit;

// make qunit api global
extend( global, QUnit.api );
<|endoftext|>


<commit_before>$(document).ready(function(){
  $('#Add').on('click',Addrow);
  $('tbody').on('click','.remove',remove);
  $('#removeAll').on('click',removeAll);
  $('body').on('change keyup','.score,.forgiveness',Calc);
  $('body').on('focus','.forgiveness',function(){$(this).val('');});
  $('#results').on('click',results);
});
//add btn that appends new rows to tbody
function Addrow() {
  var number = ($('tbody tr').length)+1;
  var tr = '
'+
              ''+number+'	'+
              '<input type="text" placeholder="Student Name" class="form-control name">	'+
              '<input type="number" placeholder="Score" class="form-control score">	'+
              '<input type="number" placeholder="Forgiveness" class="form-control forgiveness" value="0">	'+
              '	'+
              '	'+
              '<input type="button" class="btn btn-primary btn-sm remove" value="Remove">	'+
            '
'
  $('tbody').append(tr);
}
//remove btn that remove the row which  is in it
function remove()
{
  $(this).parent().parent().remove();
  refresh_number();
  total_average();
}
//remove btn that will remove All rows
function removeAll()
{
  $('tbody tr').remove();
}
//this func will sum score and forgiveness in each row and then
function Calc()
{
  this_row = $(this);
  refresh(); //this func will clear the status to their default state for new value change...
  var score = parseInt(this_row.parent().parent().find('.score').val(),10),
      forgiveness = parseInt(this_row.parent().parent().find('.forgiveness').val(),10);
  final = score + forgiveness;
  this_row.parent().parent().find('.final').html(final);
  total_average();
  status();
}

function total_average() {
  var scoreAll = $('.final');
  var total = 0;
  var num = 0;
//  scoreAll.each(function(i,e){ //although each func is faster than loop but for practicing i'll use a loop instead
//    var num = parseInt($(this).html(),10);
//    total += num;
//  });
  for(i=0;i<scoreAll.length;i++)
  {
    var num = parseInt($(scoreAll[i]).text(),10);
    total += num;
    var total_state = $('.total').html();
  }
  average  = total / $('tbody tr').length;
  if(!isNaN(total) && total != '' && !isNaN(average) && average != '') $('.total').html('Total : ' + total + '
' + 'Average : ' + average );
}

function status() {
  var status = this_row.parent().parent().find('.status');
  if(final<=20)
  {
    switch(true)
    {

      case (final>=10) : status.html('passed').removeClass('undefine').addClass('alert-success'); break;
      case (final<10) : status.html('failed').removeClass('undefine').addClass('alert-danger'); break;
    }
  }

  if(final>20)
  {
    this_row.parent().parent().find('.final').html('more than 20 ?').addClass('alert alert-danger');
  }
}

function refresh () {
  var status = this_row.parent().parent().find('.status');
  status.removeClass('alert-success').removeClass('alert-danger').html('').addClass('undefine');
  this_row.parent().parent().find('.final').removeClass('alert alert-danger');
  $('.total').html('Total : ' + '');
}

function results() {
  results ='';
  var all_rows = $('tbody tr').length;
  var passed = $('tbody .alert-success').length;
  var failed = $('tbody .alert-danger').length;
  var undefine = $('tbody .undefine').length;
  var all = $('tbody tr');
  if(undefine == 0)
  {
    switch(true)
    {
      case (passed==0) : results = 'Average of these ' + all_rows +' scores is ' + average+ ' and All ' + failed +' students unfortunately have failed.'; break;
      case (failed==0) : results = 'Average of these ' + all_rows +' scores is ' + average+ ' and All ' + passed + ' student have passed the exem. hooooora'; break;
      case (passed!=0 && failed!=0) : results = 'Average of these ' + all_rows +' scores is ' + average + ' and ' + passed + ' students have passed and ' + failed + ' students unfortunately have failed.';break;
    }
  }
  if(undefine !=0) results = 'please enter remaining '+ undefine + ' students info or remove them. thanks!';
  alert(results);

}

function refresh_number() {
  var trs = $('tbody tr');
  var trs_len =  $('tbody tr').length;
  for(i=0;i<=trs_len;i++)
  {
    $($(trs[i])).find('.num').html(i+1);
  }
}
<commit_msg>Popup box now fully working<commit_after>$(document).ready(function(){
  $('#Add').on('click',Addrow);
  $('tbody').on('click','.remove',remove);
  $('#removeAll').on('click',removeAll);
  $('body').on('change keyup','.score,.forgiveness',Calc);
  $('body').on('focus','.forgiveness',function(){$(this).val('');});
  $('#results').on('click',results);
  $('.popup_background,#exit').on('click',function(){$('.popup_background,.popup_box').fadeOut(500);});
});
//add btn that appends new rows to tbody
function Addrow() {
  var number = ($('tbody tr').length)+1;
  var tr = '
'+
              ''+number+'	'+
              '<input type="text" placeholder="Student Name" class="form-control name">	'+
              '<input type="number" placeholder="Score" class="form-control score">	'+
              '<input type="number" placeholder="Forgiveness" class="form-control forgiveness" value="0">	'+
              '	'+
              '	'+
              '<input type="button" class="btn btn-primary btn-sm remove" value="Remove">	'+
            '
'
  $('tbody').append(tr);
}
//remove btn that remove the row which  is in it
function remove()
{
  $(this).parent().parent().remove();
  refresh_number();
  total_average();
}
//remove btn that will remove All rows
function removeAll()
{
  $('tbody tr').remove();
}
//this func will sum score and forgiveness in each row and then
function Calc()
{
  this_row = $(this);
  refresh(); //this func will clear the status to their default state for new value change...
  var score = parseInt(this_row.parent().parent().find('.score').val(),10),
      forgiveness = parseInt(this_row.parent().parent().find('.forgiveness').val(),10);
  final = score + forgiveness;
  this_row.parent().parent().find('.final').html(final);
  total_average();
  status();
}

function total_average() {
  var scoreAll = $('.final');
  var total = 0;
  var num = 0;
//  scoreAll.each(function(i,e){ //although each func is faster than loop but for practicing i'll use a loop instead
//    var num = parseInt($(this).html(),10);
//    total += num;
//  });
  for(i=0;i<scoreAll.length;i++)
  {
    var num = parseInt($(scoreAll[i]).text(),10);
    total += num;
    var total_state = $('.total').html();
  }
  average  = total / $('tbody tr').length;
  if(!isNaN(total) && total != '' && !isNaN(average) && average != '') $('.total').html('Total : ' + total + '
' + 'Average : ' + average );
}

function status() {
  var status = this_row.parent().parent().find('.status');
  if(final<=20)
  {
    switch(true)
    {

      case (final>=10) : status.html('passed').removeClass('undefine').addClass('alert-success'); break;
      case (final<10) : status.html('failed').removeClass('undefine').addClass('alert-danger'); break;
    }
  }

  if(final>20)
  {
    this_row.parent().parent().find('.final').html('more than 20 ?').addClass('alert alert-danger');
  }
}

function refresh () {
  var status = this_row.parent().parent().find('.status');
  status.removeClass('alert-success').removeClass('alert-danger').html('').addClass('undefine');
  this_row.parent().parent().find('.final').removeClass('alert alert-danger');
  $('.total').html('Total : ' + '');
}

function results() {
  $('#result_screen').removeClass('alert-danger');//refresh for new value!!
  results ='';
  var all_rows = $('tbody tr').length;
  var passed = $('tbody .alert-success').length;
  var failed = $('tbody .alert-danger').length;
  var undefine = $('tbody .undefine').length;
  var all = $('tbody tr');
  if(undefine == 0)
  {
    switch(true)
    {
      case (passed==0) : results = 'Average of these ' + all_rows +' scores is ' + average+ ' and All ' + failed +' students unfortunately have failed.'; break;
      case (failed==0) : results = 'Average of these ' + all_rows +' scores is ' + average+ ' and All ' + passed + ' student have passed the exem. hooooora';  break;
      case (passed!=0 && failed!=0) : results = 'Average of these ' + all_rows +' scores is ' + average + ' and ' + passed + ' students have passed and ' + failed + ' students unfortunately have failed.';break;
    }
  }
  if(undefine !=0)
  {
    results = 'Please enter remaining '+ undefine + ' students info or remove them. thanks!';
    $('#result_screen').removeClass('alert-success').addClass('alert-danger');
  }
  $('.popup_background,.popup_box').fadeIn(500);
  $('#result_screen').html(results);

}

function refresh_number() {
  var trs = $('tbody tr');
  var trs_len =  $('tbody tr').length;
  for(i=0;i<=trs_len;i++)
  {
    $($(trs[i])).find('.num').html(i+1);
  }
}
<|endoftext|>


<commit_before>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */
var express = require('express');
var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');
var sha1 = require('sha1');

var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
var firebase = new Firebase('https://todium.firebaseio.com/');

var addFirebaseTracker = function(firebase, id, info_hash, src) {
    var globalTrackerId = sha1(sha1(id) + sha1(info_hash));
    console.log('addFirebaseTracker', id, info_hash, src, globalTrackerId);

    var campaignTracker = firebase.child('campaigns').child(id).child('trackers').child(info_hash);
    console.log('globalTrackerId', globalTrackerId);
    var globalTracker = firebase.child('trackers').child(globalTrackerId);

    globalTracker.child('time').once('value', function (valueSnapshot) {
        if (!valueSnapshot.val()) {
            globalTracker.child('time').set(new Date().getTime());
        }
    });
    globalTracker.child('info_hash').set(info_hash);
    globalTracker.child('campaign').set(id);

    campaignTracker.set(globalTrackerId);
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
    var torrentId = sha1(tracker);
    console.log('addFirebaseTorrent', id, tracker, torrent, torrentId);
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    firebase.child('torrent').child(torrentId).child('tracker').set(tracker);
    firebase.child('torrent').child(torrentId).child('torrent').set(torrent);
    firebase.child('torrent').child(torrentId).child('campaign').set(id);
    return 'http://torrent.todium.com/' + torrentId + '.torrent';
}

var addLinkRequest = function(firebase, id, src) {
    console.log('addLinkRequest', id, src);

    var ret = new q.defer();
    if(src.match(/[0-9A-Fa-f]{40}/)) {
        var info_hash = src;
        var tracker = addFirebaseTracker(firebase, id, info_hash, MAGNET_LINK_IDENTIFIER + info_hash);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = MAGNET_LINK_IDENTIFIER + info_hash + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(url);
    } else if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = src.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
};

var authenticationRequest = function(id, secret) {
    console.log('authenticationRequest', id, secret);
    var ret = new q.defer();
    firebase.child('campaigns').child(id).child('secret').once('value', function(secretSnapshot) {
        if(secretSnapshot.val() !== secret) {
            var error = 'invalid campaign secret';
            console.error(error);
            return ret.reject(error);
        } else {
            return ret.resolve();
        }
    });
    return ret.promise;
};

var port = process.env.PORT || 5051;
console.log('port', port);
var app = express();
app.use(function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');

    // intercept OPTIONS method
    if ('OPTIONS' == req.method) {
      res.send(200);
    } else {
      next();
    }
});
app.use(express.bodyParser());
var handlePost = function(req, res) {
    var abort = function(err) {
        res.send(400, err);
    };

    // if we don't have a url argument, then lets bail
    if(!req.body.hasOwnProperty('id') ||
        !req.body.hasOwnProperty('secret') ||
        !req.body.hasOwnProperty('src')
    ) {
        abort('src/id/secret mandatory');
        return;
    }
    var id = req.body['id'];
    var secret = req.body['secret'];
    var src = req.body['src'];

    console.log('id', id);
    console.log('secret', secret);
    console.log('src', src);

    authenticationRequest(id, secret).then(function() {
        addLinkRequest(firebase, id, src).then(function(link) {
            res.send(200, link);
        }, abort);
    }, abort);
};
app.post('/', handlePost).post('/tracker', handlePost).listen(port, function() {
    console.log('Listening on ' + port);
});<commit_msg>refactoring and linting<commit_after>(function () {
    "use strict";

    var express = require('express');
    var Firebase = require('./firebase-node');
    var http = require('http');
    var URL = require('url');
    var q = require('q');
    var request = require('request');
    var sha1 = require('sha1');

    var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
    var root = new Firebase('https://todium.firebaseio.com/');

    var addFirebaseTracker = function(firebase, id, info_hash, src) {
        var globalTrackerId = sha1(sha1(id) + sha1(info_hash));
        console.log('addFirebaseTracker', id, info_hash, src, globalTrackerId);

        var campaignTracker = firebase.child('campaigns').child(id).child('trackers').child(info_hash);
        console.log('globalTrackerId', globalTrackerId);
        var globalTracker = firebase.child('trackers').child(globalTrackerId);

        globalTracker.child('time').once('value', function (valueSnapshot) {
            if (!valueSnapshot.val()) {
                globalTracker.child('time').set(new Date().getTime());
            }
        });
        globalTracker.child('info_hash').set(info_hash);
        globalTracker.child('campaign').set(id);

        campaignTracker.set(globalTrackerId);
        return globalTracker;
    };

    var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
        var torrentId = sha1(tracker);
        console.log('addFirebaseTorrent', id, tracker, torrent, torrentId);
        var file = URL.parse(torrent).pathname;
        file = file.substring(file.lastIndexOf('/'));
        firebase.child('torrent').child(torrentId).child('tracker').set(tracker);
        firebase.child('torrent').child(torrentId).child('torrent').set(torrent);
        firebase.child('torrent').child(torrentId).child('campaign').set(id);
        return 'http://torrent.todium.com/' + torrentId + '.torrent';
    };

    var addInfoHashLinkRequest = function (firebase, id, src) {
        var info_hash = src;
        var tracker = addFirebaseTracker(firebase, id, info_hash, MAGNET_LINK_IDENTIFIER + info_hash);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = MAGNET_LINK_IDENTIFIER + info_hash + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        return q.resolve(url);
    };

    var addMagnetLinkRequest = function (firebase, id, src) {
        console.log('replacing magnet link');
        var info_hash = src.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        return q.resolve(trackable);
    };

    var addTorrentLinkRequest = function (firebase, id, src) {
        var ret = q.defer();
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
        return ret.promise;
    };

    var addLinkRequest = function(firebase, id, src) {
        console.log('addLinkRequest', id, src);

        if(src.match(/[0-9A-Fa-f]{40}/)) {
            return addInfoHashLinkRequest(firebase, id, src);
        } else if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
            return addMagnetLinkRequest(firebase, id, src);
        } else {
            return addTorrentLinkRequest(firebase, id, src);
        }
    };

    var authenticationRequest = function(firebase, id, secret) {
        console.log('authenticationRequest', id, secret);
        var ret = new q.defer();
        firebase.child('campaigns').child(id).child('secret').once('value', function(secretSnapshot) {
            if(secretSnapshot.val() !== secret) {
                var error = 'invalid campaign secret';
                console.error(error);
                return ret.reject(error);
            } else {
                return ret.resolve();
            }
        });
        return ret.promise;
    };

    var port = process.env.PORT || 5000;
    var app = express();
    app.use(function(req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
        res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');

        // intercept OPTIONS method
        if (req.method === 'OPTIONS') {
            res.send(200);
        } else {
            next();
        }
    });
    app.use(express.bodyParser());
    var handlePost = function(req, res) {
        var abort = function(err) {
            res.send(400, err);
        };

        // if we don't have a url argument, then lets bail
        if(!req.body.hasOwnProperty('id') ||
            !req.body.hasOwnProperty('secret') ||
            !req.body.hasOwnProperty('src')
        ) {
            abort('src/id/secret mandatory');
            return;
        }
        var id = req.body['id'];
        var secret = req.body['secret'];
        var src = req.body['src'];

        console.log('id', id);
        console.log('secret', secret);
        console.log('src', src);

        authenticationRequest(root, id, secret).then(function() {
            addLinkRequest(root, id, src).then(function(link) {
                res.send(200, link);
            }, abort);
        }, abort);
    };
    app.post('/', handlePost).post('/tracker', handlePost).listen(port, function() {
        console.log('Listening on ' + port);
    });
}());<|endoftext|>


<commit_before>define([],function () {
    var kkbd = [
        "А", "а", "Ä", "ä", "Б", "б", "В", "в", "Г", "г", "Д", "д", "Е", "е", "Ё", "ё", "Ӗ", "ӗ", "Ж", "ж", "З", "з", "И", "и", "Й", "й", "К", "к", "Л", "л", "ԕ", "ԕ", "М", "м", "Н", "н", "Ҥ", "ҥ", "О", "о", "Ӧ", "ӧ", "П", "п", "Р", "р", "Ԗ", "ԗ", "С", "с", "Т", "т", "У", "у", "Ӱ", "ӱ", "Ф", "ф", "Х", "х", "Ц", "ц", "Ч", "ч", "Ш", "ш", "Щ", "щ", "Ъ", "ъ", "Ы", "ы", "Ӹ", "ӹ", "Ь", "ь", "Э", "э", "Ӭ", "ӭ", "Ю", "ю", "Я", "я", "Ы̆", "ы̆", "Ŏ", "ŏ"
    ];
    var lkbd = [
        "A", "a", "Ä", "ä", "Å", "å", "B", "в", "C", "c", "Ç", "ç", "D", "d", "Ə", "ә", "E", "e", "F", "f", "G", "g", "Y", "y", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o", "Ö", "ö", "P", "p", "R", "r", "S", "s", "Ş", "ş", "T", "t", "U", "u", "V", "v", "X", "x", "Z", "z", "Ƶ", "ƶ", "Ь", "ь", "rx", "lh"
    ];
    var latinkbd = [
        "A", "a", "B", "b", "C", "c", "D", "d", "E", "e", "F", "f", "G", "g", "H", "h", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o",
        "P", "p", "Q", "q", "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y", "Z", "z"
    ];
    var livkbd = [
        "A",  "Ä", "B", "D",  "Ḑ", "E", "F", "G", "H", "I", "J", "K", "L", "Ļ", "M", "N", "Ņ", "O", "Ȯ", "Õ", "P", "R", "Ŗ", "S", "Š", "T", "Ț", "U", "V", "Z", "Ž", "C", "Q", "W", "X", "Y",
        "a", "ä", "b", "d", "ḑ", "e", "f", "g", "h", "i", "j", "k", "l", "ļ", "m", "n", "ņ", "o", "ȯ", "õ", "p", "r", "ŗ", "s", "š", "t", "ț", "u", "v", "z", "ž", "c", "q", "w", "x", "y",
        "Ā", "Ǟ", "Ē", "Ī", "Ō", "Ȱ", "Ȭ", "Ū",
        "ā", "ǟ", "ē", "ī", "ō", "ȱ", "ȭ", "ū"
    ];

    return {

        buttons: [
            /*
            {
                id:'edit-layout',
                toggle:true,
                icon:'icon-th',
                title:'Edit page layout',
                modes:['page'],
                event:'toggleShowLayout',
            }
            */
            {
                id: 'download-xml',
                index: 44,
                toggle: false,
                active: false,
                icon: 'icon-download',
                title: 'Download page XML',
                modes: ['page'],
                event: 'downloadXml'
            },
            {
                id:'show-highlight',
                index: 33,
                toggle:true,
                active:true,
                icon:'icon-font',
                title:'Show editor word highlight',
                modes:['page'],
                event: 'toggleShowHighlight'
            },
            {
                id:'back',
                index: 3,
                toggle: false,
                icon: 'icon-backward',
                title: 'Go back to the collection',
                modes:['page'],
                event: 'goBackToCollection'
            },
            {
                id:'zoom-in',
                index: 11,
                toggle:false,
                icon:'icon-zoom-in',
                title:'Zoom in',
                modes:['page'],
                event:'zoomIn'
            },

            {
                id:'zoom-out',
                index: 12,
                toggle:false,
                icon:'icon-zoom-out',
                title:'Zoom out',
                modes:['page'],
                event:'zoomOut'

            },

            {
                id:'pan-zoom',
                index: 13,
                toggle:true,
                icon:'icon-move',
                title:'Mouse wheel pan/zoom',
                modes:['page'],
                event:'panZoom'
            },

            {
                id:'layout-selector',
                index: 52,
                toggle:true,
                active: false,
                icon:'icon-repeat',
                title:'Horizontal / vertical layout',
                modes:['page'],
                event:'changeLayout'
            },
            {
                id:"save",
                index: 62,
                toggle:false,
                text:"Save",
                title:"Save",
                modes:["page"],
                event: 'saveDocument'
            },
            {
                id:'toggle-linebreaks',
                index: 21,
                toggle:true,
                active:true,
                icon:'icon-align-left',
                title:'Line break after each text line',
                modes:['page'],
                event:'toggleLineBreak'
            },

            {
                id:'highlight-editor-word',
                index: 22,
                toggle:true,
                active:false,
                icon:'icon-star',
                title:'Highlight word under cursor in editor',
                modes:['page'],
                event:'highlightEditorWord'
            },

            {
                id:'show-saved-changes',
                index: 23,
                toggle:true,
                active:true,
                icon:'icon-check',
                title:'Show unsaved changes',
                modes:['page'],
                event:'showSavedChanges'
            },

            {
                id:'show-original-changes',
                index: 24,
                toggle:true,
                active:true,
                icon:'icon-edit',
                title:'Show changes made to original',
                modes:['page'],
                event:'showOriginalChanges'
            },

            {
                id:'show-language',
                index: 25,
                toggle:true,
                active:true,
                icon:'icon-globe',
                title:'Show language of words',
                modes:['page'],
                event:'showLanguage'
            },

        ],

        shortcuts : [

            {
                code: 113,
                modes: ['page'],
                event: 'panZoom'
            },
            {
                code: 33,
                modes: ['page'],
                event: 'pagePrevious'
            },

            {
                code: 34,
                modes: ['page'],
                event: 'pageNext'
            },
        ],
        "selected_language": "fi",
        "languages": [
            {
                "code":"liv",
                "name": "Livonian",
                "keyboard": livkbd
            },
            {
                "code":"fi",
                "name":"Finnish",
                "keyboard":lkbd
            },
            {
                "code":"ers",
                "name":"Erzya",
                "keyboard":kkbd
            },
            {
                "code":"moks",
                "name":"Moksha",
                "keyboard":kkbd
            },
            {
                "code":"veps",
                "name":"Veps",
                "keyboard":lkbd
            },
            {
                "code":"ingrian",
                "name":"Ingrian",
                "keyboard":lkbd
            },
            {
                "code":"meadowmari",
                "name":"Meadow Mari",
                "keyboard":kkbd
            },
            {
                "code":"hillmari",
                "name":"Hill Mari",
                "keyboard":kkbd
            },
            {
                "code":"ru",
                "name":"Russian",
                "keyboard":kkbd
            },
            {
                "code": "oldfi",
                "name": "Old Literary Finnish",
                "keyboard": lkbd
            },
            {
                "code": "la",
                "name": "Latin",
                "keyboard": latinkbd
            }
        ],
        "urls": {
            "docBase": '/api/id/<id>/pages/'
        }
    };

});
<commit_msg>selkup keyboard<commit_after>define([],function () {
    var kkbd = [
        "А", "а", "Ä", "ä", "Б", "б", "В", "в", "Г", "г", "Д", "д", "Е", "е", "Ё", "ё", "Ӗ", "ӗ", "Ж", "ж", "З", "з", "И", "и", "Й", "й", "К", "к", "Л", "л", "ԕ", "ԕ", "М", "м", "Н", "н", "Ҥ", "ҥ", "О", "о", "Ӧ", "ӧ", "П", "п", "Р", "р", "Ԗ", "ԗ", "С", "с", "Т", "т", "У", "у", "Ӱ", "ӱ", "Ф", "ф", "Х", "х", "Ц", "ц", "Ч", "ч", "Ш", "ш", "Щ", "щ", "Ъ", "ъ", "Ы", "ы", "Ӹ", "ӹ", "Ь", "ь", "Э", "э", "Ӭ", "ӭ", "Ю", "ю", "Я", "я", "Ы̆", "ы̆", "Ŏ", "ŏ"
    ];
    var lkbd = [
        "A", "a", "Ä", "ä", "Å", "å", "B", "в", "C", "c", "Ç", "ç", "D", "d", "Ə", "ә", "E", "e", "F", "f", "G", "g", "Y", "y", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o", "Ö", "ö", "P", "p", "R", "r", "S", "s", "Ş", "ş", "T", "t", "U", "u", "V", "v", "X", "x", "Z", "z", "Ƶ", "ƶ", "Ь", "ь", "rx", "lh"
    ];
    var latinkbd = [
        "A", "a", "B", "b", "C", "c", "D", "d", "E", "e", "F", "f", "G", "g", "H", "h", "I", "i", "J", "j", "K", "k", "L", "l", "M", "m", "N", "n", "O", "o",
        "P", "p", "Q", "q", "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y", "Z", "z"
    ];
    var livkbd = [
        "A",  "Ä", "B", "D",  "Ḑ", "E", "F", "G", "H", "I", "J", "K", "L", "Ļ", "M", "N", "Ņ", "O", "Ȯ", "Õ", "P", "R", "Ŗ", "S", "Š", "T", "Ț", "U", "V", "Z", "Ž", "C", "Q", "W", "X", "Y",
        "a", "ä", "b", "d", "ḑ", "e", "f", "g", "h", "i", "j", "k", "l", "ļ", "m", "n", "ņ", "o", "ȯ", "õ", "p", "r", "ŗ", "s", "š", "t", "ț", "u", "v", "z", "ž", "c", "q", "w", "x", "y",
        "Ā", "Ǟ", "Ē", "Ī", "Ō", "Ȱ", "Ȭ", "Ū",
        "ā", "ǟ", "ē", "ī", "ō", "ȱ", "ȭ", "ū"
    ];

    var nenetskbd = [

    ];

    var selkbd = [
        "A", "а", "ӄн", "Ӱ", "ӱ", "Å",  "å", "Б", "б", "Л", "л", "Х", "x", "В", "в", "ль", "Ц", "ц", "в", "М", "м", "Ч", "ч", "в", "Н", "н", "ч",
        "Г", "нь", "Ш", "ш", "Д", "д", "Ӈ", "ӈ", "ш", "Е", "е", "О", "о", "Щ", "щ", "e", "Ӧ", "ӧ", "щ", "Ё", "ё", "Œ", "œ", "Ъ", "ъ", "Ж", "ж",
        "Ә", "ә", "Ы", "ы", "З", "з", "ә", "Ь", "ь", "И", "и", "П", "п", "Э", "э", "Й", "й", "Р", "р", "Ю", "ю", "йо", "С", "с", "ю", "К", "к",
        "Т", "т", "Я", "я", "Ӄ", "ӄ", "У", "у", "я"
    ];

    return {

        buttons: [
            /*
            {
                id:'edit-layout',
                toggle:true,
                icon:'icon-th',
                title:'Edit page layout',
                modes:['page'],
                event:'toggleShowLayout',
            }
            */
            {
                id: 'download-xml',
                index: 44,
                toggle: false,
                active: false,
                icon: 'icon-download',
                title: 'Download page XML',
                modes: ['page'],
                event: 'downloadXml'
            },
            {
                id:'show-highlight',
                index: 33,
                toggle:true,
                active:true,
                icon:'icon-font',
                title:'Show editor word highlight',
                modes:['page'],
                event: 'toggleShowHighlight'
            },
            {
                id:'back',
                index: 3,
                toggle: false,
                icon: 'icon-backward',
                title: 'Go back to the collection',
                modes:['page'],
                event: 'goBackToCollection'
            },
            {
                id:'zoom-in',
                index: 11,
                toggle:false,
                icon:'icon-zoom-in',
                title:'Zoom in',
                modes:['page'],
                event:'zoomIn'
            },

            {
                id:'zoom-out',
                index: 12,
                toggle:false,
                icon:'icon-zoom-out',
                title:'Zoom out',
                modes:['page'],
                event:'zoomOut'

            },

            {
                id:'pan-zoom',
                index: 13,
                toggle:true,
                icon:'icon-move',
                title:'Mouse wheel pan/zoom',
                modes:['page'],
                event:'panZoom'
            },

            {
                id:'layout-selector',
                index: 52,
                toggle:true,
                active: false,
                icon:'icon-repeat',
                title:'Horizontal / vertical layout',
                modes:['page'],
                event:'changeLayout'
            },
            {
                id:"save",
                index: 62,
                toggle:false,
                text:"Save",
                title:"Save",
                modes:["page"],
                event: 'saveDocument'
            },
            {
                id:'toggle-linebreaks',
                index: 21,
                toggle:true,
                active:true,
                icon:'icon-align-left',
                title:'Line break after each text line',
                modes:['page'],
                event:'toggleLineBreak'
            },

            {
                id:'highlight-editor-word',
                index: 22,
                toggle:true,
                active:false,
                icon:'icon-star',
                title:'Highlight word under cursor in editor',
                modes:['page'],
                event:'highlightEditorWord'
            },

            {
                id:'show-saved-changes',
                index: 23,
                toggle:true,
                active:true,
                icon:'icon-check',
                title:'Show unsaved changes',
                modes:['page'],
                event:'showSavedChanges'
            },

            {
                id:'show-original-changes',
                index: 24,
                toggle:true,
                active:true,
                icon:'icon-edit',
                title:'Show changes made to original',
                modes:['page'],
                event:'showOriginalChanges'
            },

            {
                id:'show-language',
                index: 25,
                toggle:true,
                active:true,
                icon:'icon-globe',
                title:'Show language of words',
                modes:['page'],
                event:'showLanguage'
            },

        ],

        shortcuts : [

            {
                code: 113,
                modes: ['page'],
                event: 'panZoom'
            },
            {
                code: 33,
                modes: ['page'],
                event: 'pagePrevious'
            },

            {
                code: 34,
                modes: ['page'],
                event: 'pageNext'
            },
        ],
        "selected_language": "fi",
        "languages": [
            {
                "code":"liv",
                "name": "Livonian",
                "keyboard": livkbd
            },
            {
                "code":"fi",
                "name":"Finnish",
                "keyboard":lkbd
            },
            {
                "code":"ers",
                "name":"Erzya",
                "keyboard":kkbd
            },
            {
                "code":"moks",
                "name":"Moksha",
                "keyboard":kkbd
            },
            {
                "code":"veps",
                "name":"Veps",
                "keyboard":lkbd
            },
            {
                "code":"ingrian",
                "name":"Ingrian",
                "keyboard":lkbd
            },
            {
                "code":"meadowmari",
                "name":"Meadow Mari",
                "keyboard":kkbd
            },
            {
                "code":"hillmari",
                "name":"Hill Mari",
                "keyboard":kkbd
            },
            {
                "code":"ru",
                "name":"Russian",
                "keyboard":kkbd
            },
            {
                "code": "oldfi",
                "name": "Old Literary Finnish",
                "keyboard": lkbd
            },
            {
                "code": "la",
                "name": "Latin",
                "keyboard": latinkbd
            },
            {
                "code": "yrk",
                "name": "nenets",
                "keyboard": nenetskbd

            },
            {
                "code": "sel",
                "name": "Selkup",
                "keyboard": selkbd
            }
        ],
        "urls": {
            "docBase": '/api/id/<id>/pages/'
        }
    };

});
<|endoftext|>