<commit_before>var
  intelHex = require('intel-hex'),
  Stream   = require('stream').Stream,
  util     = require('util');

var out = module.exports = {};

var d = function(c) {
  return (c + '').charCodeAt(0);
};

out.Flasher = function(serialport, options) {
  var that = this;
  this.options = options || {};
  this.sp = serialport;
  this.signature = this.options.signature || 'LUFACDC';

  if (this.options.debug) {
    this.sp.on('data', function(d) {
      process.stdout.write(' -> ');

      for (var i=0; i<d.length; i++) {
        var c = d.toString().substring(i, i+1);
        if (c.charCodeAt(0) < 32 || c.charCodeAt(0) > 126) {
          c = '.';
        }

        process.stdout.write(c + ' [' + d.readUInt8(i).toString(16) + '] ');
      }
      process.stdout.write('\n');
    });
  }

  this.c = function(value, fn) {
    that.cmds.push({
      value : value,
      callback : function(data) {
        fn && fn(data);
      }
    });
    return this;
  }

  this.flashChunkSize = 0;
  this.bytes = [];
  this.cmds = [];
};

out.Flasher.prototype = {
  run : function(fn) {
    var that = this;
    process.nextTick(function() {
      if (that.running) { return; }
      var cmd = that.cmds.shift();

      if (cmd) {
        running = true;
        that.options.debug && process.stdout.write('Send: ' + cmd.value);
        that.sp.once('data', function(d) {
          that.running = false;
          cmd.callback(d);

          process.nextTick(function() {
            if (that.cmds.length > 0) {
              that.run(fn);
            } else {
              fn && fn();
            }
          });
        });

        that.sp.write(cmd.value);
      }
    });
  },

  prepare : function(fn) {
    var that = this;
    this.c('S', function(d) {
          if (d.toString() !== that.signature) {
            fn(new Error('Invalid device signature; expecting: ' + that.signature + ' received: ' + d.toString()));
          }
        })
        .c('V')
        .c('v')
        .c('p')
        .c('a')
        .c('b', function(d) {
          that.flashChunkSize = d.readUInt8(2);
        })
        .c('t')
        .c('TD')
        .c('P')
        .c('F')
        .c('F')
        .c('F')
        .c('N')
        .c('N')
        .c('N')
        .c('Q')
        .c('Q')
        .c('Q')
        .c([d('A'), 0x03, 0xfc])
        .c([d('g'), 0x00, 0x01, d('E')])
        .c([d('A'), 0x03, 0xff])
        .c([d('g'), 0x00, 0x01, d('E')])
        .c([d('A'), 0x03, 0xff])
        .c([d('g'), 0x00, 0x01, d('E')])
        .c([d('A'), 0x03, 0xff])
        .c([d('g'), 0x00, 0x01, d('E')])

    this.run(function() {
      fn(null, that);
    });
  },

  erase : function(fn) {
    this.c('e', function() {
      fn && fn();
    }) // erase

    this.run();
  },

  program : function(fullString, fn) {

    var
      that = this,
      converter,
      bytes = [];

    this.totalBytes = 0;

    this.c([d('A'), 0x00, 0x00], function() {
      converter = intelHex.parse(fullString);

      that.totalBytes = converter.data.length;
      // buffer the bytes so we can push them in the expected size on 'end'
      Array.prototype.push.apply(bytes, converter.data);

      that.options.debug && console.log('programming', bytes.length, 'bytes');
      that.chunksSent = [];

      for (var i=0; i<bytes.length; i+=that.flashChunkSize) {
        var chunk = Array.prototype.slice.call(converter.data.slice(i, i+that.flashChunkSize));
        that.chunksSent.push(chunk);
        that.c([d('B'), 0x00, chunk.length, d('F')].concat(chunk));
      }
    });

    this.run(function() { fn && fn() });
  },

  verify : function(fn) {
    var that = this;
    // compare flash on device with the chunks we sent
    this.c([d('A'), 0x00, 0x00], function() {

      var
        index = 0,
        compare = function(deviceData) {

          var localChunk = that.chunksSent[index];
          index++;

          if (index >= that.chunksSent.length) {
            fn && fn();
            return;
          }

          // Quick check to make sure the lengths match
          if (localChunk.length !== deviceData.length) {
            fn(new Error(
              "Flashed content length differs! local:" + localChunk.length +
              ' vs device: ' + deviceData.length
            ));
          }

          localChunk.forEach(function(val, idx) {
            if (val !== deviceData.readUInt8(idx)) {
              fn(new Error('Firmware on the device does not match local data'));
            }
          });

          process.nextTick(function() {
            var readSize = that.flashChunkSize;
            that.options.debug && console.log(that.totalBytes - index*that.flashChunkSize);
            if (that.totalBytes - index*that.flashChunkSize < that.flashChunkSize) {
              readSize = that.totalBytes - index*that.flashChunkSize;
            }
            that.c([d('g'), 0x00, readSize, d('F')], compare);
            that.run();
          });
        };

      that.options.debug && console.log('\n\nVerifying flash..')

      that.c([d('g'), 0x00, that.flashChunkSize, d('F')], compare);
      that.run();
    });
    that.run();
  },

  fuseCheck :  fuseCheck = function(fn) {
    this.options.debug && console.log('checking fuses');
    // fuse check
    this.c('F')
        .c('F')
        .c('F')
        .c('N')
        .c('N')
        .c('N')
        .c('Q')
        .c('Q')
        .c('Q')
        .c('L')
        .c('E');

    this.run(function() {
      fn();
    });
  }
};

out.init = function(serialport, options, fn) {
  if (typeof options === 'function' && !fn) {
    fn = options;
    options = {};
  }

  var flasher = new out.Flasher(serialport, options);
  flasher.prepare(fn);
};
<commit_msg>more consistent spacing in content length verification error string<commit_after>var
  intelHex = require('intel-hex'),
  Stream   = require('stream').Stream,
  util     = require('util');

var out = module.exports = {};

var d = function(c) {
  return (c + '').charCodeAt(0);
};

out.Flasher = function(serialport, options) {
  var that = this;
  this.options = options || {};
  this.sp = serialport;
  this.signature = this.options.signature || 'LUFACDC';

  if (this.options.debug) {
    this.sp.on('data', function(d) {
      process.stdout.write(' -> ');

      for (var i=0; i<d.length; i++) {
        var c = d.toString().substring(i, i+1);
        if (c.charCodeAt(0) < 32 || c.charCodeAt(0) > 126) {
          c = '.';
        }

        process.stdout.write(c + ' [' + d.readUInt8(i).toString(16) + '] ');
      }
      process.stdout.write('\n');
    });
  }

  this.c = function(value, fn) {
    that.cmds.push({
      value : value,
      callback : function(data) {
        fn && fn(data);
      }
    });
    return this;
  }

  this.flashChunkSize = 0;
  this.bytes = [];
  this.cmds = [];
};

out.Flasher.prototype = {
  run : function(fn) {
    var that = this;
    process.nextTick(function() {
      if (that.running) { return; }
      var cmd = that.cmds.shift();

      if (cmd) {
        running = true;
        that.options.debug && process.stdout.write('Send: ' + cmd.value);
        that.sp.once('data', function(d) {
          that.running = false;
          cmd.callback(d);

          process.nextTick(function() {
            if (that.cmds.length > 0) {
              that.run(fn);
            } else {
              fn && fn();
            }
          });
        });

        that.sp.write(cmd.value);
      }
    });
  },

  prepare : function(fn) {
    var that = this;
    this.c('S', function(d) {
          if (d.toString() !== that.signature) {
            fn(new Error('Invalid device signature; expecting: ' + that.signature + ' received: ' + d.toString()));
          }
        })
        .c('V')
        .c('v')
        .c('p')
        .c('a')
        .c('b', function(d) {
          that.flashChunkSize = d.readUInt8(2);
        })
        .c('t')
        .c('TD')
        .c('P')
        .c('F')
        .c('F')
        .c('F')
        .c('N')
        .c('N')
        .c('N')
        .c('Q')
        .c('Q')
        .c('Q')
        .c([d('A'), 0x03, 0xfc])
        .c([d('g'), 0x00, 0x01, d('E')])
        .c([d('A'), 0x03, 0xff])
        .c([d('g'), 0x00, 0x01, d('E')])
        .c([d('A'), 0x03, 0xff])
        .c([d('g'), 0x00, 0x01, d('E')])
        .c([d('A'), 0x03, 0xff])
        .c([d('g'), 0x00, 0x01, d('E')])

    this.run(function() {
      fn(null, that);
    });
  },

  erase : function(fn) {
    this.c('e', function() {
      fn && fn();
    }) // erase

    this.run();
  },

  program : function(fullString, fn) {

    var
      that = this,
      converter,
      bytes = [];

    this.totalBytes = 0;

    this.c([d('A'), 0x00, 0x00], function() {
      converter = intelHex.parse(fullString);

      that.totalBytes = converter.data.length;
      // buffer the bytes so we can push them in the expected size on 'end'
      Array.prototype.push.apply(bytes, converter.data);

      that.options.debug && console.log('programming', bytes.length, 'bytes');
      that.chunksSent = [];

      for (var i=0; i<bytes.length; i+=that.flashChunkSize) {
        var chunk = Array.prototype.slice.call(converter.data.slice(i, i+that.flashChunkSize));
        that.chunksSent.push(chunk);
        that.c([d('B'), 0x00, chunk.length, d('F')].concat(chunk));
      }
    });

    this.run(function() { fn && fn() });
  },

  verify : function(fn) {
    var that = this;
    // compare flash on device with the chunks we sent
    this.c([d('A'), 0x00, 0x00], function() {

      var
        index = 0,
        compare = function(deviceData) {

          var localChunk = that.chunksSent[index];
          index++;

          if (index >= that.chunksSent.length) {
            fn && fn();
            return;
          }

          // Quick check to make sure the lengths match
          if (localChunk.length !== deviceData.length) {
            fn(new Error(
              "Flashed content length differs! local: " + localChunk.length +
              ' vs device: ' + deviceData.length
            ));
          }

          localChunk.forEach(function(val, idx) {
            if (val !== deviceData.readUInt8(idx)) {
              fn(new Error('Firmware on the device does not match local data'));
            }
          });

          process.nextTick(function() {
            var readSize = that.flashChunkSize;
            that.options.debug && console.log(that.totalBytes - index*that.flashChunkSize);
            if (that.totalBytes - index*that.flashChunkSize < that.flashChunkSize) {
              readSize = that.totalBytes - index*that.flashChunkSize;
            }
            that.c([d('g'), 0x00, readSize, d('F')], compare);
            that.run();
          });
        };

      that.options.debug && console.log('\n\nVerifying flash..')

      that.c([d('g'), 0x00, that.flashChunkSize, d('F')], compare);
      that.run();
    });
    that.run();
  },

  fuseCheck :  fuseCheck = function(fn) {
    this.options.debug && console.log('checking fuses');
    // fuse check
    this.c('F')
        .c('F')
        .c('F')
        .c('N')
        .c('N')
        .c('N')
        .c('Q')
        .c('Q')
        .c('Q')
        .c('L')
        .c('E');

    this.run(function() {
      fn();
    });
  }
};

out.init = function(serialport, options, fn) {
  if (typeof options === 'function' && !fn) {
    fn = options;
    options = {};
  }

  var flasher = new out.Flasher(serialport, options);
  flasher.prepare(fn);
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

YUI.add('proxymanager-tests', function (Y, NAME) {

        //TODO - Stub portchecker

        var path = require('path'),
            http = require('http'),
            log4js = require("log4js"),
            logger = log4js.getLogger("ProxyManagerTests"),
            arrowRoot = path.join(__dirname, '../../../../'),
            ProxyManager = require("../../../../lib/proxy/proxymanager.js"),
            portchecker = require("../../../../ext-lib/portchecker"),
            localhostip = require('../../../../arrow_server/arrowservermanager').getLocalhostIPAddress(),
            suite = new Y.Test.Suite(NAME),
            A = Y.Assert,
            self = this;

        /**
         * If port is available, proxy server should be started
         */
        function testSendRequestToProxyServer() {

            //TODO - Not complete
            var
            // TODO - Check the first available port here starting from minPort
                minPort = 10801,
                maxPort = 10900,
                hostName = "localhost",
                routerJsonPath = __dirname + "/config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {}),
                options,
                req;

            function createProxyServerRequest(minPort, maxPort, hostName, callback) {

                proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                    A.areNotEqual(proxyHostMsg, 'localhost:' + minPort, 'Proxy host should not match');
                    A.areEqual(proxyHostMsg, localhostip + ':' + minPort, 'Proxy host doesn\'t match');
                    callback();

                });
            }

            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');

            createProxyServerRequest(minPort, maxPort, hostName, function () {

                function onRequest(request, response) {

                    response.writeHead(200, {"Content-Type": "text/plain"});
                    response.write("Hello world");
                    response.end();

                }

                portchecker.getFirstAvailable(11000, 11100, "localhost", function (p) {

//                http.createServer(onRequest).listen(p);

                    options = {
                        host: hostName,
                        port: minPort,
                        path: '/',
                        method: 'GET'
                    };

                    req = http.request(options, function (res) {
                        res.setEncoding('utf8');
                        res.on('data', function (chunk) {
                            console.log('BODY: ' + chunk);
                        });
                    });

                    req.on('error', function (e) {
                        console.log('problem with request: ' + e.message);
                    });

                    // write data to request body
                    req.write('my data\n');
                    req.end();

                });

            });

        }

        /**
         * If port is available, proxy server should be started
         */
        function testClientSideInstrumentOnProxyServer() {

            // TODO - Check the first available port here starting from minPort
            var minPort = 10901,
                maxPort = 11000,
                hostName = "localhost",
                routerJsonPath = __dirname + "/config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {}),
                options,
                req;

            function createProxyServerRequest(minPort, maxPort, hostName, callback) {

                proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                    A.areNotEqual(proxyHostMsg, 'localhost:' + minPort, 'Proxy host should not match');
                    A.areEqual(proxyHostMsg, localhostip + ':' + minPort, 'Proxy host doesn\'t match');
                    callback();

                });
            }

            createProxyServerRequest(minPort, maxPort, hostName, function () {

                options = {
                    host: hostName,
                    port: minPort,
                    path: '/proxyCoverage?sid=12345',
                    method: 'GET'
                };

                function sentToCoverage(method, data) {
                    options.method = method;
                    req = http.request(options, function (res) {
                        res.setEncoding('utf8');
                        res.on('data', function (chunk) {
                            console.log('BODY: ' + chunk);
                        });
                    });

                    req.on('error', function (e) {
                        console.log('problem with request: ' + e.message);
                    });

                    // write data to request body
                    req.write(data);
                    req.end();
                }

                sentToCoverage("GET", "");
                sentToCoverage("OPTIONS", "");
                sentToCoverage("POST", ""); // should be catch
                sentToCoverage("POST", JSON.stringify({coverage: {}, sid: "12345", origin: "nourl"}));
                sentToCoverage("GET", ""); // get again

            });

        }

        // test proxy http client
        function testHttpClientOnProxyServer(test) {

            // TODO - Check the first available port here starting from minPort
            var minPort = 11001,
                maxPort = 11100,
                hostName = "localhost",
                router = {},
                proxyManager = new ProxyManager(router, {}),
                options,
                req;

            // test instrument

            function onRequest(request, response) {
                var url = require('url');
                if (url.parse(request.url).pathname == '/index.html') {
                    response.writeHead(200, {"Content-Type": "text/html"});
                    response.write("");

                } else if (url.parse(request.url).pathname == '/index.js') {
                    response.writeHead(200, {"Content-Type": "text/javascript"});
                    response.write("var a=1;");

                } else if (url.parse(request.url).pathname == '/index2.js') {
                    response.writeHead(200, {"Content-Type": "text/javascript","content-encoding":"gzip"});
                    response.write("\n");

                } else if (url.parse(request.url).pathname == '/index3.js') {
                    response.writeHead(200, {"Content-Type": "text/javascript","content-encoding":"deflate"});
                    response.write("\n");

                }else if (url.parse(request.url).pathname == '/index.jpeg') {
                    response.writeHead(200, {"Content-Type": "image/jpeg"});
                    response.write("iamapic");

                }  else {
                    response.writeHead(200, {"Content-Type": "text/plain"});
                    response.write("Hello js");

                }
                response.end();
            }

            portchecker.getFirstAvailable(11000, 11100, "localhost", function (p) {

                    http.createServer(onRequest).listen(p);

                    proxyManager.proxyConfig = {
                        "localhost": {
                            "newHost": hostName + ":" + p,
                            "headers": [
                                { "param": "user-agent",
                                    "value": "Mozilla5.0"
                                }
                            ],
                            "record": true
                        },
                        "coverage": {
                            "clientSideCoverage": true,
                            "coverageExclude": ["^http://yui.yahooapis.com.*\\.js$"]
                        }
                    };
                    global.currentSessionid = global.currentSessionid || '12345';
                    proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                        A.areNotEqual(proxyHostMsg, 'localhost:' + minPort, 'Proxy host should not match');
                        A.areEqual(proxyHostMsg, localhostip + ':' + minPort, 'Proxy host doesn\'t match');

                        options = {
                            host: hostName,
                            port: minPort,
                            method: 'GET'
                        };
                        var body = '';

                        function sentToTmpServer(path, data) {
                            options.path = path;
                            req = http.request(options, function (res) {
                                res.setEncoding('utf8');
                                res.on('data', function (chunk) {
                                    body += chunk;
                                    console.log('BODY: ' + chunk);
                                });
                            });

                            req.on('error', function (e) {
                                console.log('problem with request: ' + e.message);
                            });

                            // write data to request body
                            req.write(data);
                            req.end();
                        }

                        sentToTmpServer('/index.html', "get index");

                        sentToTmpServer('/index.js', "get js");
                        sentToTmpServer('/index2.js', "get js2");
                        sentToTmpServer('/index3.js', "get js3");
                        sentToTmpServer('/index.jpeg', "get jpeg");

                    });

                }

            )
            ;

        }

        /*
         The router file's path is valid
         */
        function testRouterValidJsonPath() {

            var routerJsonPath = "./config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {});
            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');
        }

        /*
         The router file's path is invalid
         */

        function testRouterInvalidJsonPath() {
            var routerJsonPath = ".invalidJson",
                proxyManager = new ProxyManager(routerJsonPath, {});
            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');
        }

        /*
         The router file's path is null - i.e no file specified
         */

        function testNoJsonPath() {
            var proxyManager = new ProxyManager(null, {});
            A.isNotNull(proxyManager.proxyConfig, 'Router Json Path shall be null');
        }

        /**
         * If port is not available, proxy server cant be started
         */
        function testPortsNotAvailable() {

            var
                minPort = 10701,
                maxPort = 10800,
                hostName = "localhost",
                server,
                proxyManager = new ProxyManager(null, {});

            portchecker.getFirstAvailable(minPort, maxPort, hostName, function (p, host) {

                if (p === -1) {
                    callback("Error : No free ports found for Proxy Server on " + host + " between " + minPort + " and " + maxPort);
                } else {

                    minPort = p;
                    maxPort = p;

                    server = http.createServer(function (request, response) {

                    }).listen(minPort, hostName, function () {

                            proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                                A.areEqual(proxyHostMsg, 'Error : No free ports found for Proxy Server on ' + localhostip + ' between ' + minPort + ' and ' + maxPort);
                                server.close(function () {
                                });

                            });

                        });
                }
            });

        }

        /**
         * If port is available, proxy server should be started
         */
        function testPortsAvailable(test) {

            var
            // TODO - Check the first available port here starting from minPort
                minPort = 10601,
                maxPort = 10700,
                hostName = "localhost",
                routerJsonPath = __dirname + "/config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {}),
                availablePort;

            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');

            proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {
                test.resume(function () {

                    availablePort = proxyHostMsg.split(':');
                    YUITest = Y.Test;
                    A.areEqual(proxyHostMsg, localhostip + ':' + availablePort[1], 'Proxy host doesn\'t match');
                });

                availablePort = proxyHostMsg.split(":");

            });

            test.wait(5000);

        }

        /**
         *
         */
        function testWriteLog() {

            var proxyManager = new ProxyManager(null, {}),
                fs = require("fs"),
                proxyFileData,
                timestamp = new Date().getTime(),
                proxyLogfile = "proxy_" + timestamp + ".log",
                proxyMsg = "This is proxy log" + timestamp,
                data;

            proxyManager.fileName = proxyLogfile;

            proxyManager.writeLog(proxyMsg);

            data = fs.readFileSync(proxyManager.fileName, 'utf8');
            A.areEqual(proxyMsg + '\n', data, 'Proxy logs doesn\'t match - expected :' + proxyMsg + '\n' + ' , got this:' + data);
            fs.unlinkSync(proxyManager.fileName);

        }

        /**
         * Test get options
         */
        function testGetOptions() {

            var
                proxyManager = new ProxyManager(__dirname + "/config/router.json", {"coverage": true}),

                request = {
                    'headers': { 'host': 'yahoo.com',
                        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) Gecko/20100101 Firefox/16.0',
                        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                        'accept-language': 'en-US,en;q=0.5',
                        'accept-encoding': 'gzip, deflate',
                        'proxy-connection': 'keep-alive',
                        'referer': 'http://sports.yahoo.com/',
                        'cookie': 'B=9csi6n98gtaiv&b=3&s=rt'
                    },
                    'url': 'http://www.yahoo.com:4080/'
                },
                router = {
                    "yahoo.com": {
                        "newHost": "def.yahoo.com",
                        "headers": [
                            { "param": "user-agent",
                                "value": "Mozilla5.0"
                            },
                            { "param": "accept",
                                "value": "text/json"
                            },
                            { "param": "accept-language",
                                "value": "es-ES;q=0.5"
                            },
                            { "param": "accept-encoding",
                                "value": "gzip, deflate"
                            },
                            { "param": "proxy-connection",
                                "value": "keep-alive"
                            },
                            { "param": "referer",
                                "value": "ref.yahoo.com"
                            },
                            { "param": "cookie",
                                "value": "ProxyCookie"
                            }
                        ],
                        "record": true
                    },
                    "coverage": {
                        "clientSideCoverage": true,
                        "coverageExclude": []
                    }
                },

                options = proxyManager.getOptions(router, request);

            A.areEqual(options.host, "def.yahoo.com", "Host doesnt match");
            A.areEqual(options.port, "4080", "Port doesnt match");
            A.areEqual(options.path, "/", "Path doesnt match");
            A.areEqual(options.headers.host, "yahoo.com", "Headers host doesnt match");
            A.areEqual(options.headers["user-agent"], "Mozilla5.0", "Headers - user agent doesnt match");
            A.areEqual(options.headers.accept, "text/json", "Headers - accept doesnt match");
            A.areEqual(options.headers["accept-language"], "es-ES;q=0.5", "Headers - accept-language doesnt match");
            A.areEqual(options.headers["accept-encoding"], "gzip, deflate", "Headers - accept-encoding doesnt match");
            A.areEqual(options.headers["proxy-connection"], "keep-alive", "Headers - proxy-connection doesnt match");
            A.areEqual(options.headers.referer, "ref.yahoo.com", "Headers - referer doesnt match");
            A.areEqual(options.headers.cookie, "ProxyCookie", "Headers - cookie doesnt match");

//        A.areEqual(options.coverage, true, "coverage field not set");
//        A.areEqual(options.coverage.clientSideCoverage, true, "coverage flag not set to true");
//        A.areEqual(options.coverage.coverageExclude.length, 0, "coverage exclude not set");

        }

        suite.add(new Y.Test.Case({

            'setUp': function () {
                //logger.info('>>>setup Proxymanager tests');
            },

            'tearDown': function () {
                //logger.info('>>>teardown Proxymanager tests');
            },

            'test proxy manager No Ports Available': function () {
                testPortsNotAvailable();
            },

            'ignore:test proxy manager Send Request to Proxy Server': function () {
                testSendRequestToProxyServer();
            },

            'test proxy manager Ports Available': function () {
                var test = this;
                testPortsAvailable(test);
            },

            'test proxy manager No Json Path': function () {
                testNoJsonPath();
            },

            'test proxy manager valid Json Path': function () {
                testRouterValidJsonPath();
            },

            'test proxy manager invalid Json path': function () {
                testRouterInvalidJsonPath();
            },

            'test proxy manager writeLog': function () {
                testWriteLog();
            },

            'test proxy manager get options': function () {
                testGetOptions();
            },

            'test proxy manager instruement ': function () {
                var test = this;
                testClientSideInstrumentOnProxyServer(test);
            },
            'test proxy manager client request ': function () {
                var test = this;
                testHttpClientOnProxyServer(test);
            }

        }));

        Y.Test.Runner.add(suite);
    },
    '0.0.1', {requires: ['test']}
)
;

<commit_msg>remove ignored tests<commit_after>/*
 * Copyright (c) 2012-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

YUI.add('proxymanager-tests', function (Y, NAME) {

        //TODO - Stub portchecker

        var path = require('path'),
            http = require('http'),
            log4js = require("log4js"),
            logger = log4js.getLogger("ProxyManagerTests"),
            arrowRoot = path.join(__dirname, '../../../../'),
            ProxyManager = require("../../../../lib/proxy/proxymanager.js"),
            portchecker = require("../../../../ext-lib/portchecker"),
            localhostip = require('../../../../arrow_server/arrowservermanager').getLocalhostIPAddress(),
            suite = new Y.Test.Suite(NAME),
            A = Y.Assert,
            self = this;

        /**
         * If port is available, proxy server should be started
         */
        function testSendRequestToProxyServer() {

            //TODO - Not complete
            var
            // TODO - Check the first available port here starting from minPort
                minPort = 10801,
                maxPort = 10900,
                hostName = "localhost",
                routerJsonPath = __dirname + "/config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {}),
                options,
                req;

            function createProxyServerRequest(minPort, maxPort, hostName, callback) {

                proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                    A.areNotEqual(proxyHostMsg, 'localhost:' + minPort, 'Proxy host should not match');
                    A.areEqual(proxyHostMsg, localhostip + ':' + minPort, 'Proxy host doesn\'t match');
                    callback();

                });
            }

            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');

            createProxyServerRequest(minPort, maxPort, hostName, function () {

                function onRequest(request, response) {

                    response.writeHead(200, {"Content-Type": "text/plain"});
                    response.write("Hello world");
                    response.end();

                }

                portchecker.getFirstAvailable(11000, 11100, "localhost", function (p) {

//                http.createServer(onRequest).listen(p);

                    options = {
                        host: hostName,
                        port: minPort,
                        path: '/',
                        method: 'GET'
                    };

                    req = http.request(options, function (res) {
                        res.setEncoding('utf8');
                        res.on('data', function (chunk) {
                            console.log('BODY: ' + chunk);
                        });
                    });

                    req.on('error', function (e) {
                        console.log('problem with request: ' + e.message);
                    });

                    // write data to request body
                    req.write('my data\n');
                    req.end();

                });

            });

        }

        /**
         * If port is available, proxy server should be started
         */
        function testClientSideInstrumentOnProxyServer() {

            // TODO - Check the first available port here starting from minPort
            var minPort = 10901,
                maxPort = 11000,
                hostName = "localhost",
                routerJsonPath = __dirname + "/config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {}),
                options,
                req;

            function createProxyServerRequest(minPort, maxPort, hostName, callback) {

                proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                    A.areNotEqual(proxyHostMsg, 'localhost:' + minPort, 'Proxy host should not match');
                    A.areEqual(proxyHostMsg, localhostip + ':' + minPort, 'Proxy host doesn\'t match');
                    callback();

                });
            }

            createProxyServerRequest(minPort, maxPort, hostName, function () {

                options = {
                    host: hostName,
                    port: minPort,
                    path: '/proxyCoverage?sid=12345',
                    method: 'GET'
                };

                function sentToCoverage(method, data) {
                    options.method = method;
                    req = http.request(options, function (res) {
                        res.setEncoding('utf8');
                        res.on('data', function (chunk) {
                            console.log('BODY: ' + chunk);
                        });
                    });

                    req.on('error', function (e) {
                        console.log('problem with request: ' + e.message);
                    });

                    // write data to request body
                    req.write(data);
                    req.end();
                }

                sentToCoverage("GET", "");
                sentToCoverage("OPTIONS", "");
                sentToCoverage("POST", ""); // should be catch
                sentToCoverage("POST", JSON.stringify({coverage: {}, sid: "12345", origin: "nourl"}));
                sentToCoverage("GET", ""); // get again

            });

        }

        // test proxy http client
        function testHttpClientOnProxyServer(test) {

            // TODO - Check the first available port here starting from minPort
            var minPort = 11001,
                maxPort = 11100,
                hostName = "localhost",
                router = {},
                proxyManager = new ProxyManager(router, {}),
                options,
                req;

            // test instrument

            function onRequest(request, response) {
                var url = require('url');
                if (url.parse(request.url).pathname == '/index.html') {
                    response.writeHead(200, {"Content-Type": "text/html"});
                    response.write("");

                } else if (url.parse(request.url).pathname == '/index.js') {
                    response.writeHead(200, {"Content-Type": "text/javascript"});
                    response.write("var a=1;");

                } else if (url.parse(request.url).pathname == '/index2.js') {
                    response.writeHead(200, {"Content-Type": "text/javascript","content-encoding":"gzip"});
                    response.write("\n");

                } else if (url.parse(request.url).pathname == '/index3.js') {
                    response.writeHead(200, {"Content-Type": "text/javascript","content-encoding":"deflate"});
                    response.write("\n");

                }else if (url.parse(request.url).pathname == '/index.jpeg') {
                    response.writeHead(200, {"Content-Type": "image/jpeg"});
                    response.write("iamapic");

                }  else {
                    response.writeHead(200, {"Content-Type": "text/plain"});
                    response.write("Hello js");

                }
                response.end();
            }

            portchecker.getFirstAvailable(11000, 11100, "localhost", function (p) {

                    http.createServer(onRequest).listen(p);

                    proxyManager.proxyConfig = {
                        "localhost": {
                            "newHost": hostName + ":" + p,
                            "headers": [
                                { "param": "user-agent",
                                    "value": "Mozilla5.0"
                                }
                            ],
                            "record": true
                        },
                        "coverage": {
                            "clientSideCoverage": true,
                            "coverageExclude": ["^http://yui.yahooapis.com.*\\.js$"]
                        }
                    };
                    global.currentSessionid = global.currentSessionid || '12345';
                    proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                        A.areNotEqual(proxyHostMsg, 'localhost:' + minPort, 'Proxy host should not match');
                        A.areEqual(proxyHostMsg, localhostip + ':' + minPort, 'Proxy host doesn\'t match');

                        options = {
                            host: hostName,
                            port: minPort,
                            method: 'GET'
                        };
                        var body = '';

                        function sentToTmpServer(path, data) {
                            options.path = path;
                            req = http.request(options, function (res) {
                                res.setEncoding('utf8');
                                res.on('data', function (chunk) {
                                    body += chunk;
                                    console.log('BODY: ' + chunk);
                                });
                            });

                            req.on('error', function (e) {
                                console.log('problem with request: ' + e.message);
                            });

                            // write data to request body
                            req.write(data);
                            req.end();
                        }

                        sentToTmpServer('/index.html', "get index");

                        sentToTmpServer('/index.js', "get js");
                        sentToTmpServer('/index2.js', "get js2");
                        sentToTmpServer('/index3.js', "get js3");
                        sentToTmpServer('/index.jpeg', "get jpeg");

                    });

                }

            )
            ;

        }

        /*
         The router file's path is valid
         */
        function testRouterValidJsonPath() {

            var routerJsonPath = "./config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {});
            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');
        }

        /*
         The router file's path is invalid
         */

        function testRouterInvalidJsonPath() {
            var routerJsonPath = ".invalidJson",
                proxyManager = new ProxyManager(routerJsonPath, {});
            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');
        }

        /*
         The router file's path is null - i.e no file specified
         */

        function testNoJsonPath() {
            var proxyManager = new ProxyManager(null, {});
            A.isNotNull(proxyManager.proxyConfig, 'Router Json Path shall be null');
        }

        /**
         * If port is not available, proxy server cant be started
         */
        function testPortsNotAvailable() {

            var
                minPort = 10701,
                maxPort = 10800,
                hostName = "localhost",
                server,
                proxyManager = new ProxyManager(null, {});

            portchecker.getFirstAvailable(minPort, maxPort, hostName, function (p, host) {

                if (p === -1) {
                    callback("Error : No free ports found for Proxy Server on " + host + " between " + minPort + " and " + maxPort);
                } else {

                    minPort = p;
                    maxPort = p;

                    server = http.createServer(function (request, response) {

                    }).listen(minPort, hostName, function () {

                            proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {

                                A.areEqual(proxyHostMsg, 'Error : No free ports found for Proxy Server on ' + localhostip + ' between ' + minPort + ' and ' + maxPort);
                                server.close(function () {
                                });

                            });

                        });
                }
            });

        }

        /**
         * If port is available, proxy server should be started
         */
        function testPortsAvailable(test) {

            var
            // TODO - Check the first available port here starting from minPort
                minPort = 10601,
                maxPort = 10700,
                hostName = "localhost",
                routerJsonPath = __dirname + "/config/router.json",
                proxyManager = new ProxyManager(routerJsonPath, {}),
                availablePort;

            A.areEqual(routerJsonPath, proxyManager.proxyConfig, 'Router jsonpath doesn\'t match');

            proxyManager.runRouterProxy(minPort, maxPort, hostName, function (proxyHostMsg) {
                test.resume(function () {

                    availablePort = proxyHostMsg.split(':');
                    YUITest = Y.Test;
                    A.areEqual(proxyHostMsg, localhostip + ':' + availablePort[1], 'Proxy host doesn\'t match');
                });

                availablePort = proxyHostMsg.split(":");

            });

            test.wait(5000);

        }

        /**
         *
         */
        function testWriteLog() {

            var proxyManager = new ProxyManager(null, {}),
                fs = require("fs"),
                proxyFileData,
                timestamp = new Date().getTime(),
                proxyLogfile = "proxy_" + timestamp + ".log",
                proxyMsg = "This is proxy log" + timestamp,
                data;

            proxyManager.fileName = proxyLogfile;

            proxyManager.writeLog(proxyMsg);

            data = fs.readFileSync(proxyManager.fileName, 'utf8');
            A.areEqual(proxyMsg + '\n', data, 'Proxy logs doesn\'t match - expected :' + proxyMsg + '\n' + ' , got this:' + data);
            fs.unlinkSync(proxyManager.fileName);

        }

        /**
         * Test get options
         */
        function testGetOptions() {

            var
                proxyManager = new ProxyManager(__dirname + "/config/router.json", {"coverage": true}),

                request = {
                    'headers': { 'host': 'yahoo.com',
                        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) Gecko/20100101 Firefox/16.0',
                        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                        'accept-language': 'en-US,en;q=0.5',
                        'accept-encoding': 'gzip, deflate',
                        'proxy-connection': 'keep-alive',
                        'referer': 'http://sports.yahoo.com/',
                        'cookie': 'B=9csi6n98gtaiv&b=3&s=rt'
                    },
                    'url': 'http://www.yahoo.com:4080/'
                },
                router = {
                    "yahoo.com": {
                        "newHost": "def.yahoo.com",
                        "headers": [
                            { "param": "user-agent",
                                "value": "Mozilla5.0"
                            },
                            { "param": "accept",
                                "value": "text/json"
                            },
                            { "param": "accept-language",
                                "value": "es-ES;q=0.5"
                            },
                            { "param": "accept-encoding",
                                "value": "gzip, deflate"
                            },
                            { "param": "proxy-connection",
                                "value": "keep-alive"
                            },
                            { "param": "referer",
                                "value": "ref.yahoo.com"
                            },
                            { "param": "cookie",
                                "value": "ProxyCookie"
                            }
                        ],
                        "record": true
                    },
                    "coverage": {
                        "clientSideCoverage": true,
                        "coverageExclude": []
                    }
                },

                options = proxyManager.getOptions(router, request);

            A.areEqual(options.host, "def.yahoo.com", "Host doesnt match");
            A.areEqual(options.port, "4080", "Port doesnt match");
            A.areEqual(options.path, "/", "Path doesnt match");
            A.areEqual(options.headers.host, "yahoo.com", "Headers host doesnt match");
            A.areEqual(options.headers["user-agent"], "Mozilla5.0", "Headers - user agent doesnt match");
            A.areEqual(options.headers.accept, "text/json", "Headers - accept doesnt match");
            A.areEqual(options.headers["accept-language"], "es-ES;q=0.5", "Headers - accept-language doesnt match");
            A.areEqual(options.headers["accept-encoding"], "gzip, deflate", "Headers - accept-encoding doesnt match");
            A.areEqual(options.headers["proxy-connection"], "keep-alive", "Headers - proxy-connection doesnt match");
            A.areEqual(options.headers.referer, "ref.yahoo.com", "Headers - referer doesnt match");
            A.areEqual(options.headers.cookie, "ProxyCookie", "Headers - cookie doesnt match");

//        A.areEqual(options.coverage, true, "coverage field not set");
//        A.areEqual(options.coverage.clientSideCoverage, true, "coverage flag not set to true");
//        A.areEqual(options.coverage.coverageExclude.length, 0, "coverage exclude not set");

        }

        suite.add(new Y.Test.Case({

            'setUp': function () {
                //logger.info('>>>setup Proxymanager tests');
            },

            'tearDown': function () {
                //logger.info('>>>teardown Proxymanager tests');
            },

            'test proxy manager No Ports Available': function () {
                testPortsNotAvailable();
            },

            'test proxy manager Send Request to Proxy Server': function () {
                testSendRequestToProxyServer();
            },

            'test proxy manager Ports Available': function () {
                var test = this;
                testPortsAvailable(test);
            },

            'test proxy manager No Json Path': function () {
                testNoJsonPath();
            },

            'test proxy manager valid Json Path': function () {
                testRouterValidJsonPath();
            },

            'test proxy manager invalid Json path': function () {
                testRouterInvalidJsonPath();
            },

            'test proxy manager writeLog': function () {
                testWriteLog();
            },

            'test proxy manager get options': function () {
                testGetOptions();
            },

            'test proxy manager instruement ': function () {
                var test = this;
                testClientSideInstrumentOnProxyServer(test);
            },
            'test proxy manager client request ': function () {
                var test = this;
                testHttpClientOnProxyServer(test);
            }

        }));

        Y.Test.Runner.add(suite);
    },
    '0.0.1', {requires: ['test']}
)
;

<|endoftext|>


<commit_before><commit_msg>Changed loader to old one<commit_after><|endoftext|>


<commit_before>"use strict";

Aural.Sound.BufferMap = function(buffer, vgate, vsmoothing, vlimiting, tgate, tlimiting) {
	this.buffer = buffer;

	this.transientMap = null;
	this.volumeMap = null;

	this.setVolumeMapGate(vgate);
	this.setVolumeMapSmoothing(vsmoothing);
	this.setVolumeMapLimiting(vlimiting);
	this.setTransientMapGate(tgate);
	this.setTransientMapLimiting(tlimiting);
};

Aural.Sound.BufferMap.prototype.buffer = null;

Aural.Sound.BufferMap.prototype.volumeMap = null;
Aural.Sound.BufferMap.prototype.transientMap = null;

Aural.Sound.BufferMap.prototype.volumeMapLimiting = null;
Aural.Sound.BufferMap.prototype.volumeMapGate = null;
Aural.Sound.BufferMap.prototype.volumeMapSmoothing = null;
Aural.Sound.BufferMap.prototype.transientMapGate = null;
Aural.Sound.BufferMap.prototype.transientMapLimiting = null;

Aural.Sound.BufferMap.prototype.forceRefresh = function() {
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setVolumeMapGate = function(vgate) {
	this.volumeMapGate = Math.abs(vgate || 0);
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setVolumeMapSmoothing = function(vsmoothing) {
	this.volumeMapSmoothing = Math.abs(vsmoothing || 0);
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setVolumeMapLimiting = function(vlimiting) {
	this.volumeMapLimiting = Math.max(1, vlimiting || 1);
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setTransientMapGate = function(tgate) {
	this.transientMapGate = tgate || 0.3;
	this.transientMap = null;
};

Aural.Sound.BufferMap.prototype.setTransientMapLimiting = function(tlimiting) {
	this.transientMapLimiting = tlimiting || 2;
	this.transientMap = null;
};

Aural.Sound.BufferMap.prototype.processVolumeMap = function() {
	var previous = 0;
	var previousPeak = 0;
	var sample = 0;
	var nextSample = 0;

	var vgate = this.volumeMapGate;
	var vsmoothing = this.volumeMapSmoothing;
	var vlimiting = this.volumeMapLimiting;

	var peaks = {
		'0' : 0
	};

	var wavelets = new Aural.Sound.Wavelet.Collection(this.buffer);
	var pos = 0;
	var wavelet;
	var min = 0;
	var max = 0;
	var advance = 0;
	var duration = 0;
	var maxOffset = 0;
	for(var i = 0, l = wavelets.length; i < l; i++) {
		wavelet = wavelets.wavelets[i];

		sample = Math.pow(wavelet.maxValue * 10, 2);

		min = sample;
		max = sample;
		maxOffset = 0;
		advance = 0;
		duration = 0;

		nextSample = Math.abs(i + 1 < l ? wavelets.get(i + 1).maxValue : 0);

		if(sample < vgate) {
			sample = 0;
		}

		if(sample > previous && sample > nextSample) {
			if(Math.abs(sample - previousPeak) > vsmoothing) {
				while(sample >= max || advance < vlimiting) {
					i++;
					advance++;

					var nextSample = Math.pow(wavelets.wavelets[i].maxValue * 10, 2);

					if(max < nextSample) {
						if(nextSample - max > (vsmoothing * Math.sqrt(vlimiting))) {
							i--;
							break;
						}

						max = nextSample;
						advance = 0;
						maxOffset = duration;
					}

					if(min > nextSample) {
						min = nextSample;
					}

					sample = nextSample;
					duration+= wavelets.wavelets[i].length;
				}

				peaks[pos + maxOffset] = Math.sqrt(max) / 10;
				previousPeak = max;
			}
		}

		previous = sample;
		pos+= wavelet.length + duration;

	}

	peaks[this.buffer.length - 1] = 0;

	this.volumeMap = peaks;
};

Aural.Sound.BufferMap.prototype.getVolumeMap = function() {
	if(this.volumeMap === null) {
		this.processVolumeMap();
	}

	return this.volumeMap;
};<commit_msg>a much less brutally stupid volume map generation<commit_after>"use strict";

Aural.Sound.BufferMap = function(buffer, vgate, vsmoothing, vlimiting, tgate, tlimiting) {
	this.buffer = buffer;

	this.transientMap = null;
	this.volumeMap = null;

	this.setVolumeMapGate(vgate);
	this.setVolumeMapSmoothing(vsmoothing);
	this.setVolumeMapLimiting(vlimiting);
	this.setTransientMapGate(tgate);
	this.setTransientMapLimiting(tlimiting);
};

Aural.Sound.BufferMap.prototype.buffer = null;

Aural.Sound.BufferMap.prototype.volumeMap = null;
Aural.Sound.BufferMap.prototype.transientMap = null;

Aural.Sound.BufferMap.prototype.volumeMapLimiting = null;
Aural.Sound.BufferMap.prototype.volumeMapGate = null;
Aural.Sound.BufferMap.prototype.volumeMapSmoothing = null;
Aural.Sound.BufferMap.prototype.transientMapGate = null;
Aural.Sound.BufferMap.prototype.transientMapLimiting = null;

Aural.Sound.BufferMap.prototype.forceRefresh = function() {
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setVolumeMapGate = function(vgate) {
	this.volumeMapGate = Math.abs(vgate || 0);
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setVolumeMapSmoothing = function(vsmoothing) {
	this.volumeMapSmoothing = Math.abs(vsmoothing || 0);
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setVolumeMapLimiting = function(vlimiting) {
	this.volumeMapLimiting = Math.max(1, vlimiting || 1);
	this.transientMap = null;
	this.volumeMap = null;
};

Aural.Sound.BufferMap.prototype.setTransientMapGate = function(tgate) {
	this.transientMapGate = tgate || 0.3;
	this.transientMap = null;
};

Aural.Sound.BufferMap.prototype.setTransientMapLimiting = function(tlimiting) {
	this.transientMapLimiting = tlimiting || 2;
	this.transientMap = null;
};

Aural.Sound.BufferMap.prototype.processPolarVolumeMap = function(wavelets, polarity) {
	var peaks = {
		'0' : 0
	};

	var vgate = this.volumeMapGate;
	var vsmoothing = this.volumeMapSmoothing;
	var vlimiting = this.volumeMapLimiting;

	var min = 0;
	var max = 0;
	var advance = 0;
	var duration = 0;
	var maxOffset = 0;

	var previous = 0;
	var previousPeak = 0;
	var sample = 0;
	var nextSample = 0;
	var pos = 0;
	var wavelet;

	for(var i = 0, l = wavelets.length; i < l; i++) {
		wavelet = wavelets.wavelets[i];

		if(wavelet.polarity != polarity) {
			pos+= wavelet.length;
			continue;
		}

		sample = wavelet.maxValue;
		sample = sample > vgate ? wavelet.maxValue : 0;

		max = sample;
		maxOffset = 0;
		advance = 0;
		duration = 0;

		while(sample >= max && advance < vlimiting) {
			i++;
			advance++;

			var nextWavelet = !!wavelets.wavelets[i] ? wavelets.wavelets[i] : null;

			if(nextWavelet && nextWavelet.polarity == polarity) {
				var nextSample = nextWavelet.maxValue;

				if(max < nextSample) {
					if(nextSample - max > (Math.sqrt(vlimiting) / 10000)) {
						i--;
						break;
					}

					max = nextSample;
					advance = 0;
					maxOffset = duration;
				}

				sample = nextSample;
			}

			duration+= nextWavelet ? nextWavelet.length : 0;
		}

		peaks[pos + maxOffset] = max;

		pos+= wavelet.length + duration;
	}

	peaks[this.buffer.length - 1] = 0;

	var previousPos = 0;
	var previousPeak = 0;
	var sample = 0;

	var map = new Float32Array(this.buffer.length);

	for(var key in peaks) {
		key*=1;
		sample = peaks[key];

		sample = sample < previousPeak ? sample * (1 - vsmoothing) + previousPeak * vsmoothing : sample;

		map[key] = sample;

		var duration = key - previousPos;

		var start = previousPos;

		for(; previousPos < key; previousPos++) {
			map[previousPos] = Aural.Sound.Interpolation.linear((previousPos - start) / duration, [previousPeak, sample]);
		}

		previousPeak = sample;
		previousPos = key;
	}

	return map;
};

Aural.Sound.BufferMap.prototype.processVolumeMap = function() {
	var vgate = this.volumeMapGate;
	var vsmoothing = this.volumeMapSmoothing;
	var vlimiting = this.volumeMapLimiting;

	var wavelets = new Aural.Sound.Wavelet.Collection(this.buffer);

	var positiveMap = this.processPolarVolumeMap(wavelets, 1);
	var negativeMap = this.processPolarVolumeMap(wavelets, -1);

	//make the smooth envelope
	var volumeMap = new Float32Array(this.buffer.length);

	for(var i = 0, l = volumeMap.length; i < l; i++) {
		volumeMap[i] = Math.max(negativeMap[i], positiveMap[i]);
	}

	this.volumeMap = volumeMap;
};

Aural.Sound.BufferMap.prototype.getVolumeMap = function() {
	if(this.volumeMap === null) {
		this.processVolumeMap();
	}

	return this.volumeMap;
};

Aural.Sound.BufferMap.prototype.processTransientsMap = function() {
	var ipos = 0;
	var previous = 0;
	var sample = 0;
	var previous = 0;

	var tgate = this.transientMapGate;
	var tlimiting = this.transientMapLimiting;

	var transients = {
		0 : [0,0]
	};

	for(var i = 0, l = this.volumeMap.length; i < l; i++) {
		sample = Math.abs(this.volumeMap[i]);
		if(sample - previous > tgate) {
			var min = sample;
			var max = sample;
			var advance = 0;

			while(sample >= max || advance < tlimiting) {
				i++;
				advance++;

				sample = Math.abs(this.volumeMap[i]);

				if(max < sample) {
					max = sample;
					advance = 0;
				}

				if(min > sample) {
					min = sample;
				}
			}

			transients[i] = [min, max];
		}
		previous = sample;
	}

	transients[this.buffer.length] = [0,0];

	this.transientMap = transients;
};

Aural.Sound.BufferMap.prototype.getTransientsMap = function() {
	if(this.transientMap === null) {
		this.processTransientsMap();
	}

	return this.transientMap;
};<|endoftext|>


<commit_before>import passport from 'passport';
import {Strategy as GitHubStrategy} from 'passport-github';
import User from '../../api/models/user';
import config from '../../config';

passport.use(new GitHubStrategy({
  clientID: config.oAuth.github.clientID,
  clientSecret: config.oAuth.github.clientSecret,
  callbackURL: config.oAuth.github.callbackURL
}, (accessToken, refreshToken, profile, done) => {

  User.findOne({provider: 'github', 'social.id': profile.id}).exec().then(user => {

    if (!user) {
      user = new User({
        name: profile.displayName,
        username: profile.username,
        email: profile._json.email || '',
        provider: 'github',
        photo: profile._json.avatar_url,
        'social.id': profile.id,
        'social.info': profile._json
      });
    } else {
      user.social.info = profile._json;
      user.photo = profile._json.avatar_url;
      user.lastLogin = Date.now();
    }

    user.save().then(user => done(null, user)).catch(err => done(err));

  }).catch(err => done(err));

}));
<commit_msg>updated callback<commit_after>import passport from 'passport';
import {Strategy as GitHubStrategy} from 'passport-github';
import User from '../../api/models/user';
import config from '../../config';

passport.use(new GitHubStrategy({
  clientID: config.oAuth.github.clientID,
  clientSecret: config.oAuth.github.clientSecret,
  callbackURL: config.oAuth.github.callbackURL
}, (accessToken, refreshToken, profile, done) => {

  User.findOne({provider: 'github', 'social.id': profile.id}).exec().then(user => {

    if (!user) {
      user = new User({
        name: profile.displayName,
        username: profile.username,
        email: profile._json.email || '',
        provider: 'github',
        photo: profile._json.avatar_url,
        'social.id': profile.id,
        'social.info': profile._json
      });
    } else {
      user.social.info = profile._json;
      user.photo = profile._json.avatar_url;
    }

    user.lastLogin = Date.now();

    user.save().then(_user => {
      return done(null, _user);
    }).catch(err => done(err));

  }).catch(err => done(err));

}));
<|endoftext|>


<commit_before>if (typeof(module) !== 'undefined') {
    var turretWeapon = require("../server/turretWeaponServer.js");
    var _ = require("underscore");
    var Promise = require("bluebird");
}

pointDefenseWeapon = class extends turretWeapon {
    constructor() {
	super(...arguments);
	this._enabled = false;
    }

    findNearestValid(items) {
	var get_distance = function(a, b) {
	    return Math.pow((a.position[0] - b.position[0]), 2) +
		Math.pow((a.position[1] - b.position[1]), 2);
	};

	var distances = {};
	items.forEach(function(t) {
	    if (t.properties.vulnerableTo.includes("point defense")) {
		var dist = get_distance(t, this.source);
		distances[dist] = t;
	    }
	}.bind(this));

	var min = Math.min(...Object.keys(distances));
	if (min !== Infinity) {
	    return distances[min];
	}
	else {
	    return null;
	}
    }

    get enabled() {
	return this._enabled;
    }

    set enabled(v) {
	if (v) {
	    this._enabled = true;
	    this.firing = true;
	}
	else {
	    this._enabled = false;
	    this.firing = false;
	}
    }

    fire() {
	if (this.target) {
	    this.exitIndex = (this.exitIndex + 1) % this.exitPoints.length;
	    var position = this.exitPoints[this.exitIndex].position;
	    var x_diff = this.target.position[0] - position[0];
	    var y_diff = this.target.position[1] - position[1];
	    var directionToTarget = (Math.atan2(y_diff, x_diff) + 2*Math.PI) % (2*Math.PI);

	    if (this.checkBlindspots(directionToTarget)) {
		// Then it's in a blindspot and we can't fire
		return false;
	    }

	    var solution = this.calcFiringSolution(position);
	    var fireAngle = solution.fireAngle;
	    var hitTime = solution.hitTime;

	    // Only fire if it will hit
	    // hitTime && hitTime <= something
	    var lifetime = this.properties.duration / 30;
	    if (hitTime <= lifetime) {
		return super.fireProjectile(this.addInaccuracy(fireAngle), position);
	    }
	}
	return false;
    }

    render() {
	this.target = this.findNearestValid(this.source.targetedBy);

	super.render(...arguments);

    }
};

if (typeof(module) !== 'undefined') {
    module.exports = pointDefenseWeapon;
}
<commit_msg>Fixed point defense weapons firing sideways when there is no firing solution<commit_after>if (typeof(module) !== 'undefined') {
    var turretWeapon = require("../server/turretWeaponServer.js");
    var _ = require("underscore");
    var Promise = require("bluebird");
}

pointDefenseWeapon = class extends turretWeapon {
    constructor() {
	super(...arguments);
	this._enabled = false;
    }

    findNearestValid(items) {
	var get_distance = function(a, b) {
	    return Math.pow((a.position[0] - b.position[0]), 2) +
		Math.pow((a.position[1] - b.position[1]), 2);
	};

	var distances = {};
	items.forEach(function(t) {
	    if (t.properties.vulnerableTo.includes("point defense")) {
		var dist = get_distance(t, this.source);
		distances[dist] = t;
	    }
	}.bind(this));

	var min = Math.min(...Object.keys(distances));
	if (min !== Infinity) {
	    return distances[min];
	}
	else {
	    return null;
	}
    }

    get enabled() {
	return this._enabled;
    }

    set enabled(v) {
	if (v) {
	    this._enabled = true;
	    this.firing = true;
	}
	else {
	    this._enabled = false;
	    this.firing = false;
	}
    }

    fire() {
	if (this.target) {
	    this.exitIndex = (this.exitIndex + 1) % this.exitPoints.length;
	    var position = this.exitPoints[this.exitIndex].position;
	    var x_diff = this.target.position[0] - position[0];
	    var y_diff = this.target.position[1] - position[1];
	    var directionToTarget = (Math.atan2(y_diff, x_diff) + 2*Math.PI) % (2*Math.PI);

	    if (this.checkBlindspots(directionToTarget)) {
		// Then it's in a blindspot and we can't fire
		return false;
	    }

	    var solution = this.calcFiringSolution(position);
	    var fireAngle = solution.fireAngle;
	    var hitTime = solution.hitTime;

	    // There's a bug here where if there is no firing solution, it still fires a shot
	    // Only fire if it will hit
	    // hitTime && hitTime <= something
	    var lifetime = this.properties.duration / 30;
	    if (hitTime && (hitTime <= lifetime)) {
		return super.fireProjectile(this.addInaccuracy(fireAngle), position);
	    }
	}
	return false;
    }

    render() {
	this.target = this.findNearestValid(this.source.targetedBy);

	super.render(...arguments);

    }
};

if (typeof(module) !== 'undefined') {
    module.exports = pointDefenseWeapon;
}
<|endoftext|>


<commit_before>class Hud {

	constructor(canvas, mainCanvas) {
    this.canvas = canvas;
    this.context = canvas.getContext("2d");
	}

  resize(width, height) {
    //this.canvas.height = height;
    //this.canvas.width = width;
    this.canvas.height = 70;
    this.canvas.width = 320;
  }

  //todo add abbraction in this function
  draw(hasStarted, currentRoomConstraint, maze) {
    //maybe remove this line
    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.context.save();
    if(currentRoomConstraint.type === "o") { // duplicate from maze.js)
      this.showEndGame();
      this.drawMaze(maze.discoveredMatrix);
    } else if (maze.nbAttempts <= 0) {
      this.showLoseGame();
    } else if (hasStarted) {
      this.displayNbAttempt(maze.nbAttempts, maze.maxAttempt);
      this.drawMaze(maze.discoveredMatrix);
    } else {
      this.showGameText();
       //this.displayNbAttempt(maze.nbAttempts, maze.maxAttempt);
      //this.drawMaze(maze.discoveredMatrix);
    }
    this.context.restore();
  }

  showGameText() {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 320, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "18pt sans-serif";
    this.context.fillText("Welcome in this experiment", 10, 40);
  }

  showEndGame() {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 320, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "18pt sans-serif";
    this.context.fillText("You Win", 10, 40);
  }

  showLoseGame() {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 320, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "18pt sans-serif";
    this.context.fillText("You Lose :( refresh the page", 10, 40);
  }

  displayNbAttempt(currentAttempt, maxAttempt) {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 220, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "13pt sans-serif";
    this.context.fillText(`Attempts: ${currentAttempt} / ${maxAttempt}`, 10, 40);
  }

  drawMaze(matrix) {
    const offset = 20;
    const sizeCells = 15;
    const width = 4 * sizeCells + 2;
    const boxes = 4;
    this.context.translate(this.canvas.width/2 - width/2 + offset, 5);
    this.context.beginPath();
    this.context.lineWidth = 1;
    this.context.strokeStyle = 'black';
    this.context.fillStyle = "white";
    //draw grid
    for (var row = 0; row < boxes; row++) {
      for (var column = 0; column < boxes; column++) {
        var x = column * sizeCells;
        var y = row * sizeCells;
        this.context.rect(x, y, sizeCells, sizeCells);
        this.context.fill();
        this.context.stroke();
      }
    }
    this.context.closePath();

    //helper function
    const getColor = (val) => {
      switch (val) {
        case 0:
          return "#607D8B";
        case 1:
          return "#FF5722";
        case 2:
          return "#03A9F4";
        case 3:
          return "#8BC34A";
        default:
          return "#607D8B";
      }
    };
    //fill case
    for (var row = 0; row < boxes; row++) {
      for (var column = 0; column < boxes; column++) {
        const color = getColor(matrix[row][column]);
        this.context.fillStyle = color;
        var x = column * sizeCells;
        var y = row * sizeCells;
        this.context.fillRect(x, y, sizeCells, sizeCells);
        this.context.stroke();
      }
    }
  }
}
export default Hud;<commit_msg>change text intro<commit_after>class Hud {

	constructor(canvas, mainCanvas) {
    this.canvas = canvas;
    this.context = canvas.getContext("2d");
	}

  resize(width, height) {
    //this.canvas.height = height;
    //this.canvas.width = width;
    this.canvas.height = 70;
    this.canvas.width = 320;
  }

  //todo add abbraction in this function
  draw(hasStarted, currentRoomConstraint, maze) {
    //maybe remove this line
    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.context.save();
    if(currentRoomConstraint.type === "o") { // duplicate from maze.js)
      this.showEndGame();
      this.drawMaze(maze.discoveredMatrix);
    } else if (maze.nbAttempts <= 0) {
      this.showLoseGame();
    } else if (hasStarted) {
      this.displayNbAttempt(maze.nbAttempts, maze.maxAttempt);
      this.drawMaze(maze.discoveredMatrix);
    } else {
      this.showGameText();
       //this.displayNbAttempt(maze.nbAttempts, maze.maxAttempt);
      //this.drawMaze(maze.discoveredMatrix);
    }
    this.context.restore();
  }

  showGameText() {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 320, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "18pt sans-serif";
    this.context.fillText("Pinch to escape !!", 10, 40);
    this.context.font = "8pt sans-serif";
    this.context.fillText("LD 39", 10, 60);
  }

  showEndGame() {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 320, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "18pt sans-serif";
    this.context.fillText("You Win", 10, 40);
  }

  showLoseGame() {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 320, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "18pt sans-serif";
    this.context.fillText("You Lose :( refresh the page", 10, 40);
  }

  displayNbAttempt(currentAttempt, maxAttempt) {
    this.context.beginPath();
    this.context.fillStyle = "black";
    this.context.fillRect(0, 0, 220, 70);
    this.context.fill();
    this.context.fillStyle = "white";
    this.context.font = "13pt sans-serif";
    this.context.fillText(`Attempts: ${currentAttempt} / ${maxAttempt}`, 10, 40);
  }

  drawMaze(matrix) {
    const offset = 20;
    const sizeCells = 15;
    const width = 4 * sizeCells + 2;
    const boxes = 4;
    this.context.translate(this.canvas.width/2 - width/2 + offset, 5);
    this.context.beginPath();
    this.context.lineWidth = 1;
    this.context.strokeStyle = 'black';
    this.context.fillStyle = "white";
    //draw grid
    for (var row = 0; row < boxes; row++) {
      for (var column = 0; column < boxes; column++) {
        var x = column * sizeCells;
        var y = row * sizeCells;
        this.context.rect(x, y, sizeCells, sizeCells);
        this.context.fill();
        this.context.stroke();
      }
    }
    this.context.closePath();

    //helper function
    const getColor = (val) => {
      switch (val) {
        case 0:
          return "#607D8B";
        case 1:
          return "#FF5722";
        case 2:
          return "#03A9F4";
        case 3:
          return "#8BC34A";
        default:
          return "#607D8B";
      }
    };
    //fill case
    for (var row = 0; row < boxes; row++) {
      for (var column = 0; column < boxes; column++) {
        const color = getColor(matrix[row][column]);
        this.context.fillStyle = color;
        var x = column * sizeCells;
        var y = row * sizeCells;
        this.context.fillRect(x, y, sizeCells, sizeCells);
        this.context.stroke();
      }
    }
  }
}
export default Hud;<|endoftext|>


<commit_before>import React from 'react'
import {Nav, NavItem} from 'react-bootstrap'
import {IndexLinkContainer as Link} from 'react-router-bootstrap'

export default class extends React.Component {
	render() {
		return (
			<Nav bsStyle="pills" stacked>
				<Link to="/">
					<NavItem>Home</NavItem>
				</Link>

				<Link to="/reports/new">
					<NavItem>Submit a report</NavItem>
				</Link>

				<Link to="/reports">
					<NavItem>Reports</NavItem>
				</Link>

				{process.env.NODE_ENV === 'development' && <Link to="/graphiql">
					<NavItem>GraphQL</NavItem>
				</Link>}
			</Nav>
		)
	}
}
<commit_msg>Fetch organizations through gql and show them in the Nav<commit_after>import React from 'react'
import {Nav, NavItem, NavDropdown, MenuItem} from 'react-bootstrap'
import {IndexLinkContainer as Link} from 'react-router-bootstrap'

import API from '../api'

export default class extends React.Component {
	constructor(props) {
		super(props)
		this.state = {organizations: []}
	}

	componentDidMount() {
		API.query(`
			organizations(f:getAllOrgs, pageNum:0, pageSize:200) {
				id, name
				parentOrg { id }
			}
		`).then(data => this.setState({organizations: data.organizations.filter(org => org.parentOrg === null)}))
	}

	render() {
		return (
			<Nav bsStyle="pills" stacked>
				<Link to="/">
					<NavItem>Home</NavItem>
				</Link>

				<Link to="/reports/new">
					<NavItem>Submit a report</NavItem>
				</Link>

				<Link to="/reports">
					<NavItem>My Reports</NavItem>
				</Link>

				<NavDropdown title="Organizations" id="organizations">
					{this.state.organizations.map(org =>
						<Link to={"/organizations/" + org.id} key={org.id}>
							<MenuItem>{org.name}</MenuItem>
						</Link>
					)}
				</NavDropdown>

				{process.env.NODE_ENV === 'development' &&
					<Link to="/graphiql">
						<NavItem>GraphQL</NavItem>
					</Link>
				}
			</Nav>
		)
	}
}
<|endoftext|>


<commit_before>var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;

DS.Transaction = Ember.Object.extend({
  init: function() {
    set(this, 'buckets', {
      clean:   Ember.Map.create(),
      created: Ember.Map.create(),
      updated: Ember.Map.create(),
      deleted: Ember.Map.create()
    });
  },

  createRecord: function(type, hash) {
    var store = get(this, 'store');

    return store.createRecord(type, hash, this);
  },

  add: function(record) {
    // we could probably make this work if someone has a valid use case. Do you?
    ember_assert("Once a record has changed, you cannot move it into a different transaction", !get(record, 'isDirty'));

    var recordTransaction = get(record, 'transaction'),
        defaultTransaction = getPath(this, 'store.defaultTransaction');

    ember_assert("Models cannot belong to more than one transaction at a time.", recordTransaction === defaultTransaction);

    this.adoptRecord(record);
  },

  remove: function(record) {
    var defaultTransaction = getPath(this, 'store.defaultTransaction');

    defaultTransaction.adoptRecord(record);
  },

  /**
    @private

    This method moves a record into a different transaction without the normal
    checks that ensure that the user is not doing something weird, like moving
    a dirty record into a new transaction.

    It is designed for internal use, such as when we are moving a clean record
    into a new transaction when the transaction is committed.

    This method must not be called unless the record is clean.
  */
  adoptRecord: function(record) {
    var oldTransaction = get(record, 'transaction');

    if (oldTransaction) {
      oldTransaction.removeFromBucket('clean', record);
    }

    this.addToBucket('clean', record);
    set(record, 'transaction', this);
  },

  recordBecameDirty: function(kind, record) {
    this.removeFromBucket('clean', record);
    this.addToBucket(kind, record);
  },

  /** @private */
  addToBucket: function(kind, record) {
    var bucket = get(get(this, 'buckets'), kind),
        type = record.constructor;

    var records = bucket.get(type);

    if (!records) {
      records = Ember.OrderedSet.create();
      bucket.set(type, records);
    }

    records.add(record);
  },

  /** @private */
  removeFromBucket: function(kind, record) {
    var bucket = get(get(this, 'buckets'), kind),
        type = record.constructor;

    var records = bucket.get(type);
    records.remove(record);
  },

  recordBecameClean: function(kind, record) {
    this.removeFromBucket(kind, record);

    var defaultTransaction = getPath(this, 'store.defaultTransaction');
    defaultTransaction.adoptRecord(record);
  },

  commit: function() {
    var buckets = get(this, 'buckets');

    var iterate = function(kind, fn, binding) {
      var dirty = get(buckets, kind);

      dirty.forEach(function(type, records) {
        if (records.isEmpty()) { return; }

        var array = [];

        records.forEach(function(record) {
          record.send('willCommit');

          if (get(record, 'isPending') === false) {
            array.push(record);
          }
        });

        fn.call(binding, type, array);
      });
    };

    var commitDetails = {
      updated: {
        eachType: function(fn, binding) { iterate('updated', fn, binding); }
      },

      created: {
        eachType: function(fn, binding) { iterate('created', fn, binding); }
      },

      deleted: {
        eachType: function(fn, binding) { iterate('deleted', fn, binding); }
      }
    };

    var store = get(this, 'store');
    var adapter = get(store, '_adapter');

    var clean = get(buckets, 'clean');
    var defaultTransaction = get(store, 'defaultTransaction');

    clean.forEach(function(type, records) {
      records.forEach(function(record) {
        this.remove(record);
      }, this);
    }, this);

    if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
    else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
  },

  /**
    Rolling back a transaction resets the records that belong to
    that transaction.

    Updated records have their properties reset to the last known
    value from the persistence layer. Deleted records are reverted
    to a clean, non-deleted state. Newly created records immediately
    become deleted, and are not sent to the adapter to be persisted.

    After the transaction is rolled back, any records that belong
    to it will return to the store's default transaction, and the
    current transaction should not be used again.
  */
  rollback: function() {
    var buckets = get(this, 'buckets'),
        store = get(this, 'store'),
        dirty;

    // Loop through all of the records in each of the dirty states
    // and initiate a rollback on them. As a side effect of telling
    // the record to roll back, it should also move itself out of
    // the dirty bucket and into the clean bucket.
    ['created', 'updated', 'deleted'].forEach(function(dirtyType) {
      dirty = get(buckets, dirtyType);

      dirty.forEach(function(type, records) {
        records.forEach(function(record) {
          record.send('rollback');
        });
      });
    });

    // Now that all records in the transaction are guaranteed to be
    // clean, migrate them all to the store's default transaction.
    var clean = get(buckets, 'clean');
    var defaultTransaction = get(store, 'defaultTransaction');

    clean.forEach(function(type, records) {
      records.forEach(function(record) {
        this.remove(record);
      }, this);
    }, this);
  }
});
<commit_msg>Refactors and adds documentation to DS.Transaction<commit_after>var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;

/**
  A transaction allows you to collect multiple records into a unit of work
  that can be committed or rolled back as a group.

  For example, if a record has local modifications that have not yet
  been saved, calling `commit()` on its transaction will cause those
  modifications to be sent to the adapter to be saved. Calling
  `rollback()` on its transaction would cause all of the modifications to
  be discarded and the record to return to the last known state before
  changes were made.

  If a newly created record's transaction is rolled back, it will
  immediately transition to the deleted state.

  If you do not explicitly create a transaction, a record is assigned to
  an implicit transaction called the default transaction. In these cases,
  you can treat your application's instance of `DS.Store` as a transaction
  and call the `commit()` and `rollback()` methods on the store itself.

  Once a record has been successfully committed or rolled back, it will
  be moved back to the implicit transaction. Because it will now be in
  a clean state, it can be moved to a new transaction if you wish.

  ### Creating a Transaction

  To create a new transaction, call the `transaction()` method of your
  application's `DS.Store` instance:

      var transaction = App.store.transaction();

  This will return a new instance of `DS.Transaction` with no records
  yet assigned to it.

  ### Adding Existing Records

  Add records to a transaction using the `add()` method:

      record = App.store.find(Person, 1);
      transaction.add(record);

  Note that only records whose `isDirty` flag is `false` may be added
  to a transaction. Once modifications to a record have been made
  (its `isDirty` flag is `true`), it is not longer able to be added to
  a transaction.

  ### Creating New Records

  Because newly created records are dirty from the time they are created,
  and because dirty records can not be added to a transaction, you must
  use the `createRecord()` method to assign new records to a transaction.

  For example, instead of this:

    var transaction = store.transaction();
    var person = Person.createRecord({ name: "Steve" });

    // won't work because person is dirty
    transaction.add(person);

  Call `createRecord()` on the transaction directly:

    var transaction = store.transaction();
    transaction.createRecord(Person, { name: "Steve" });

  ### Asynchronous Commits

  Typically, all of the records in a transaction will be committed
  together. However, new records that have a dependency on other new
  records need to wait for their parent record to be saved and assigned an
  ID. In that case, the child record will continue to live in the
  transaction until its parent is saved, at which time the transaction will
  attempt to commit again.

  For this reason, you should not re-use transactions once you have committed
  them. Always make a new transaction and move the desired records to it before
  calling commit.
*/

DS.Transaction = Ember.Object.extend({
  /**
    @private

    Creates the bucket data structure used to segregate records by
    type.
  */
  init: function() {
    set(this, 'buckets', {
      clean:   Ember.Map.create(),
      created: Ember.Map.create(),
      updated: Ember.Map.create(),
      deleted: Ember.Map.create()
    });
  },

  /**
    Creates a new record of the given type and assigns it to the transaction
    on which the method was called.

    This is useful as only clean records can be added to a transaction and
    new records created using other methods immediately become dirty.

    @param {DS.Model} type the model type to create
    @param {Object} hash the data hash to assign the new record
  */
  createRecord: function(type, hash) {
    var store = get(this, 'store');

    return store.createRecord(type, hash, this);
  },

  /**
    Adds an existing record to this transaction. Only records without
    modficiations (i.e., records whose `isDirty` property is `false`)
    can be added to a transaction.

    @param {DS.Model} record the record to add to the transaction
  */
  add: function(record) {
    // we could probably make this work if someone has a valid use case. Do you?
    ember_assert("Once a record has changed, you cannot move it into a different transaction", !get(record, 'isDirty'));

    var recordTransaction = get(record, 'transaction'),
        defaultTransaction = getPath(this, 'store.defaultTransaction');

    ember_assert("Models cannot belong to more than one transaction at a time.", recordTransaction === defaultTransaction);

    this.adoptRecord(record);
  },

  /**
    Commits the transaction, which causes all of the modified records that
    belong to the transaction to be sent to the adapter to be saved.

    Once you call `commit()` on a transaction, you should not re-use it.

    When a record is saved, it will be removed from this transaction and
    moved back to the store's default transaction.
  */
  commit: function() {
    var self = this,
        iterate;

    iterate = function(bucketType, fn, binding) {
      var dirty = self.bucketForType(bucketType);

      dirty.forEach(function(type, records) {
        if (records.isEmpty()) { return; }

        var array = [];

        records.forEach(function(record) {
          record.send('willCommit');

          if (get(record, 'isPending') === false) {
            array.push(record);
          }
        });

        fn.call(binding, type, array);
      });
    };

    var commitDetails = {
      updated: {
        eachType: function(fn, binding) { iterate('updated', fn, binding); }
      },

      created: {
        eachType: function(fn, binding) { iterate('created', fn, binding); }
      },

      deleted: {
        eachType: function(fn, binding) { iterate('deleted', fn, binding); }
      }
    };

    var store = get(this, 'store');
    var adapter = get(store, '_adapter');

    this.removeCleanRecords();

    if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
    else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
  },

  /**
    Rolling back a transaction resets the records that belong to
    that transaction.

    Updated records have their properties reset to the last known
    value from the persistence layer. Deleted records are reverted
    to a clean, non-deleted state. Newly created records immediately
    become deleted, and are not sent to the adapter to be persisted.

    After the transaction is rolled back, any records that belong
    to it will return to the store's default transaction, and the
    current transaction should not be used again.
  */
  rollback: function() {
    var store = get(this, 'store'),
        dirty;

    // Loop through all of the records in each of the dirty states
    // and initiate a rollback on them. As a side effect of telling
    // the record to roll back, it should also move itself out of
    // the dirty bucket and into the clean bucket.
    ['created', 'updated', 'deleted'].forEach(function(bucketType) {
      dirty = this.bucketForType(bucketType);

      dirty.forEach(function(type, records) {
        records.forEach(function(record) {
          record.send('rollback');
        });
      });
    }, this);

    // Now that all records in the transaction are guaranteed to be
    // clean, migrate them all to the store's default transaction.
    this.removeCleanRecords();
  },

  /**
    @private

    Removes a record from this transaction and back to the store's
    default transaction.

    Note: This method is private for now, but should probably be exposed
    in the future once we have stricter error checking (for example, in the
    case of the record being dirty).

    @param {DS.Model} record
  */
  remove: function(record) {
    var defaultTransaction = getPath(this, 'store.defaultTransaction');
    defaultTransaction.adoptRecord(record);
  },

  /**
    @private

    Removes all of the records in the transaction's clean bucket.
  */
  removeCleanRecords: function() {
    var clean = this.bucketForType('clean'),
        self = this;

    clean.forEach(function(type, records) {
      records.forEach(function(record) {
        self.remove(record);
      });
    });
  },

  /**
    @private

    Returns the bucket for the given bucket type. For example, you might call
    `this.bucketForType('updated')` to get the `Ember.Map` that contains all
    of the records that have changes pending.

    @param {String} bucketType the type of bucket
    @returns Ember.Map
  */
  bucketForType: function(bucketType) {
    var buckets = get(this, 'buckets');

    return get(buckets, bucketType);
  },

  /**
    @private

    This method moves a record into a different transaction without the normal
    checks that ensure that the user is not doing something weird, like moving
    a dirty record into a new transaction.

    It is designed for internal use, such as when we are moving a clean record
    into a new transaction when the transaction is committed.

    This method must not be called unless the record is clean.

    @param {DS.Model} record
  */
  adoptRecord: function(record) {
    var oldTransaction = get(record, 'transaction');

    if (oldTransaction) {
      oldTransaction.removeFromBucket('clean', record);
    }

    this.addToBucket('clean', record);
    set(record, 'transaction', this);
  },

  /**
    @private

    Adds a record to the named bucket.

    @param {String} bucketType one of `clean`, `created`, `updated`, or `deleted`
  */
  addToBucket: function(bucketType, record) {
    var bucket = this.bucketForType(bucketType),
        type = record.constructor;

    var records = bucket.get(type);

    if (!records) {
      records = Ember.OrderedSet.create();
      bucket.set(type, records);
    }

    records.add(record);
  },

  /**
    @private

    Removes a record from the named bucket.

    @param {String} bucketType one of `clean`, `created`, `updated`, or `deleted`
  */
  removeFromBucket: function(bucketType, record) {
    var bucket = this.bucketForType(bucketType),
        type = record.constructor;

    var records = bucket.get(type);
    records.remove(record);
  },

  /**
    @private

    Called by a record's state manager to indicate that the record has entered
    a dirty state. The record will be moved from the `clean` bucket and into
    the appropriate dirty bucket.

    @param {String} bucketType one of `created`, `updated`, or `deleted`
  */
  recordBecameDirty: function(bucketType, record) {
    this.removeFromBucket('clean', record);
    this.addToBucket(bucketType, record);
  },

  /**
    @private

    Called by a record's state manager to indicate that the record has entered
    a clean state. The record will be moved from its current dirty bucket and into
    the `clean` bucket.

    @param {String} bucketType one of `created`, `updated`, or `deleted`
  */
  recordBecameClean: function(kind, record) {
    this.removeFromBucket(kind, record);

    var defaultTransaction = getPath(this, 'store.defaultTransaction');
    defaultTransaction.adoptRecord(record);
  }
});
<|endoftext|>