<commit_before>'use strict';

const fs = require('fs');
const {Readable} = require('stream');

const test = require('supertape');
const tryCatch = require('try-catch');
const stub = require('@cloudcmd/stub');
const mockRequire = require('mock-require');
const serveOnce = require('serve-once');

const fixture = {
    get: require(`${__dirname}/fixture/get`),
    getRaw: require(`${__dirname}/fixture/get-raw`),
};

const {reRequire} = mockRequire;

const restafary = require('..');

const {request} = serveOnce(restafary, {
    root: __dirname,
});

const {stringify} = JSON;
const {assign} = Object;

test('restafary: path traversal beyond root', async (t) => {
    const root = '/tmp';
    const {body} = await request.get('/fs..%2f..%2fetc/passwd', {
        options: {
            root,
        },
    });

    t.equal(body, 'Path /etc/passwd beyond root /tmp!', 'should return beyond root message');
    t.end();
});

test('restafary: path traversal', async (t) => {
    const {body} = await request.get('/fs/bin', {
        options: {
            root: '/',
        },
    });

    const [error] = tryCatch(JSON.parse, body);

    t.notOk(error, 'should not throw');
    t.end();
});

test('restafary: path traversal, not default root', async (t) => {
    const {body} = await request.get('/fs/local', {
        options: {
            root: '/usr',
        },
    });
    const [error] = tryCatch(JSON.parse, body);

    t.notOk(error, 'should not throw');
    t.end();
});

test('restafary: path traversal: "."', async (t) => {
    const notRoot = (_) => !/^\./.test(_);
    const path = fs.readdirSync(__dirname)
        .filter(notRoot)
        .pop();

    const {status} = await request.get(`/fs/${path}`);

    t.equal(status, 200, 'status code should be OK');
    t.end();
});

test('restafary: get: "raw": status', async (t) => {
    const {status} = await request.get('/fs/fixture/get-raw?raw');

    t.equal(status, 200, 'status code should be OK');
    t.end();
});

test('restafary: get: "raw": body: name', async (t) => {
    const {body} = await request.get('/fs/fixture/get-raw?raw');

    t.deepEqual(fixture.getRaw.name, JSON.parse(body).name, 'should return raw data');
    t.end();
});

test('restafary: get: "raw": body: size', async (t) => {
    const {body} = await request.get('/fs/fixture/get-raw?raw');

    t.deepEqual(fixture.getRaw.size, JSON.parse(body).size, 'should return raw data');
    t.end();
});

test('restafary: get: "raw": body: mode', async (t) => {
    const {body} = await request.get('/fs/fixture/get-raw?raw');

    t.deepEqual(fixture.getRaw.mode, JSON.parse(body).mode, 'should return raw data');
    t.end();
});

test('restafary: get: status', async (t) => {
    const {status} = await request.get('/fs/fixture/get');

    t.equal(status, 200, 'status code should be OK');
    t.end();
});

test('restafary: get: body: name', async (t) => {
    const {body} = await request.get('/fs/fixture/get', {
        type: 'json',
    });

    t.deepEqual(fixture.get.name, body.name, 'should return data');
    t.end();
});

test('restafary: get: body: size', async (t) => {
    const {body} = await request.get('/fs/fixture/get', {
        type: 'json',
    });

    t.deepEqual(fixture.get.size, body.size, 'should return data');
    t.end();
});

test('restafary: get: body: mode', async (t) => {
    const {body} = await request.get('/fs/fixture/get', {
        type: 'json',
    });

    t.deepEqual(fixture.get.mode, body.mode, 'should return data');
    t.end();
});

test('restafary: get: sort by name', async (t) => {
    const path = './';
    const files = [{
        name: '.readify.js',
        size: '3.46kb',
        date: '12.01.2017',
        owner: 'root',
        mode: 'rw- rw- r--',
    }, {
        name: 'readdir.js',
        size: '1.59kb',
        date: '12.01.2017',
        owner: 'root',
        mode: 'rw- rw- r--',
    }];

    const str = stringify({
        path,
        files,
    });

    const stream = Readable.from(str);

    assign(stream, {
        contentLength: Buffer.byteLength(str),
        type: 'directory',
    });

    const read = stub().returns(stream);

    mockRequire('win32', {
        read,
    });

    reRequire('../server/fs/get');

    const restafary = reRequire('..');
    const {request} = serveOnce(restafary, {
        root: '/',
    });

    await request.get('/fs/bin?sort=name');

    const order = 'asc';
    const sort = 'name';
    const root = '/';

    t.calledWith(read, ['/bin', {sort, order, root}], 'should call readify with sort "name"');
    t.end();
});

test('restafary: get: sort by size', async (t) => {
    const path = '';
    const files = [];

    const str = stringify({
        path,
        files,
    });

    const stream = Readable.from(str);

    assign(stream, {
        type: 'directory',
        contentLength: Buffer.byteLength(str),
    });

    const read = stub().returns(stream);

    mockRequire('win32', {
        read,
    });

    reRequire('../server/fs/get');

    const restafary = reRequire('..');
    const {request} = serveOnce(restafary, {
        root: '/',
    });

    await request.get('/fs/bin?sort=size');

    const order = 'asc';
    const sort = 'size';
    const root = '/';

    t.calledWith(read, ['/bin', {sort, order, root}], 'should call readify with sort "size"');
    t.end();
});

test('restafary: get: sort by order', async (t) => {
    const path = '';
    const files = [];

    const str = stringify({
        path,
        files,
    });

    const stream = Readable.from(str);

    assign(stream, {
        contentLength: Buffer.byteLength(str),
        type: 'directory',
    });

    const read = stub().returns(stream);

    mockRequire('win32', {
        read,
    });

    reRequire('../server/fs/get');

    const restafary = reRequire('..');
    const {request} = serveOnce(restafary, {
        root: '/',
    });

    await request.get('/fs/bin?order=desc&sort=time');

    const sort = 'time';
    const order = 'desc';
    const root = '/';

    t.calledWith(read, ['/bin', {sort, order, root}], 'should call readify with sort and order');
    t.end();
});

test('restafary: path traversal: emoji', async (t) => {
    const url = encodeURI('/fs/fixture/🎉/');
    const {body} = await request.get(url, {
        type: 'json',
        options: {
            root: __dirname,
        },
    });

    delete body.files[0].owner;
    delete body.files[0].mode;
    delete body.files[0].date;

    const expected = {
        path: '/fixture/🎉/',
        files: [{
            name: 'hello.txt',
            size: '6b',
            type: 'file',
        }],
    };

    t.deepEqual(body, expected);
    t.end();
});

test('restafary: path traversal: zip', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip/', {
        options: {
            root: __dirname,
        },
    });

    const expected = stringify({
        path: '/fixture/dir.zip/',
        files: [{
            name: 'dir',
            size: '0b',
            date: '28.08.2017',
            mode: 'rw- rw- rw-',
            type: 'directory',
            owner: 'root',
        }],
    }, null, 4);

    t.equal(body, expected);
    t.end();
});

test('restafary: path traversal: zip: download', async (t) => {
    const {headers} = await request.get('/fs/fixture/get.zip/hello.txt?download', {
        options: {
            root: __dirname,
        },
    });

    const result = headers.get('Content-Disposition');

    t.equal(result, 'attachment');
    t.end();
});

test('restafary: get: content type', async (t) => {
    const {headers} = await request.get('/fs/fixture/index.html');
    const contentType = headers.get('Content-Type');
    const expected = 'text/html; charset=utf-8';

    t.equal(contentType, expected, 'should set content type');
    t.end();
});

test('restafary: get: content type: no extension', async (t) => {
    const {headers} = await request.get('/fs/fixture/image');
    const contentType = headers.get('Content-Type');
    const expected = 'image/x-icon';

    t.equal(contentType, expected, 'should set content type');
    t.end();
});

test('restafary: get: file: content length', async (t) => {
    const {headers} = await request.get('/fs/fixture/index.html');
    const contentType = headers.get('Content-Length');
    const expected = '14';

    t.equal(contentType, expected, 'should set content length');
    t.end();
});

test('restafary: get: dir: content length', async (t) => {
    const {headers} = await request.get('/fs/fixture/dir.zip');
    const contentType = headers.get('Content-Length');
    const expected = '232';

    t.equal(contentType, expected, 'should set content length');
    t.end();
});

test('restafary: zip size', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip/dir?size', {
        options: {
            root: __dirname,
        },
    });

    const expected = '6b';

    t.equal(body, expected);
    t.end();
});

test('restafary: outer size', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip?size', {
        options: {
            root: __dirname,
        },
    });

    const expected = '232b';

    t.equal(body, expected);
    t.end();
});

test('restafary: zip: hash', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip/dir?hash', {
        options: {
            root: __dirname,
        },
    });

    const expected = 'ce5a6d585669be2da407f6b2616128b3bc755c62';

    t.equal(body, expected);
    t.end();
});

test('restafary: zip: hash: content length', async (t) => {
    const {headers} = await request.get('/fs/fixture/dir.zip/dir?hash', {
        options: {
            root: __dirname,
        },
    });

    const result = headers.get('content-length');
    const expected = '40';

    t.equal(result, expected);
    t.end();
});

<commit_msg>test(restafary) get: hash mismatch on ci<commit_after>'use strict';

const fs = require('fs');
const {Readable} = require('stream');

const test = require('supertape');
const tryCatch = require('try-catch');
const stub = require('@cloudcmd/stub');
const mockRequire = require('mock-require');
const serveOnce = require('serve-once');

const fixture = {
    get: require(`${__dirname}/fixture/get`),
    getRaw: require(`${__dirname}/fixture/get-raw`),
};

const {reRequire} = mockRequire;

const restafary = require('..');

const {request} = serveOnce(restafary, {
    root: __dirname,
});

const {stringify} = JSON;
const {assign} = Object;

test('restafary: path traversal beyond root', async (t) => {
    const root = '/tmp';
    const {body} = await request.get('/fs..%2f..%2fetc/passwd', {
        options: {
            root,
        },
    });

    t.equal(body, 'Path /etc/passwd beyond root /tmp!', 'should return beyond root message');
    t.end();
});

test('restafary: path traversal', async (t) => {
    const {body} = await request.get('/fs/bin', {
        options: {
            root: '/',
        },
    });

    const [error] = tryCatch(JSON.parse, body);

    t.notOk(error, 'should not throw');
    t.end();
});

test('restafary: path traversal, not default root', async (t) => {
    const {body} = await request.get('/fs/local', {
        options: {
            root: '/usr',
        },
    });
    const [error] = tryCatch(JSON.parse, body);

    t.notOk(error, 'should not throw');
    t.end();
});

test('restafary: path traversal: "."', async (t) => {
    const notRoot = (_) => !/^\./.test(_);
    const path = fs.readdirSync(__dirname)
        .filter(notRoot)
        .pop();

    const {status} = await request.get(`/fs/${path}`);

    t.equal(status, 200, 'status code should be OK');
    t.end();
});

test('restafary: get: "raw": status', async (t) => {
    const {status} = await request.get('/fs/fixture/get-raw?raw');

    t.equal(status, 200, 'status code should be OK');
    t.end();
});

test('restafary: get: "raw": body: name', async (t) => {
    const {body} = await request.get('/fs/fixture/get-raw?raw');

    t.deepEqual(fixture.getRaw.name, JSON.parse(body).name, 'should return raw data');
    t.end();
});

test('restafary: get: "raw": body: size', async (t) => {
    const {body} = await request.get('/fs/fixture/get-raw?raw');

    t.deepEqual(fixture.getRaw.size, JSON.parse(body).size, 'should return raw data');
    t.end();
});

test('restafary: get: "raw": body: mode', async (t) => {
    const {body} = await request.get('/fs/fixture/get-raw?raw');

    t.deepEqual(fixture.getRaw.mode, JSON.parse(body).mode, 'should return raw data');
    t.end();
});

test('restafary: get: status', async (t) => {
    const {status} = await request.get('/fs/fixture/get');

    t.equal(status, 200, 'status code should be OK');
    t.end();
});

test('restafary: get: body: name', async (t) => {
    const {body} = await request.get('/fs/fixture/get', {
        type: 'json',
    });

    t.deepEqual(fixture.get.name, body.name, 'should return data');
    t.end();
});

test('restafary: get: body: size', async (t) => {
    const {body} = await request.get('/fs/fixture/get', {
        type: 'json',
    });

    t.deepEqual(fixture.get.size, body.size, 'should return data');
    t.end();
});

test('restafary: get: body: mode', async (t) => {
    const {body} = await request.get('/fs/fixture/get', {
        type: 'json',
    });

    t.deepEqual(fixture.get.mode, body.mode, 'should return data');
    t.end();
});

test('restafary: get: sort by name', async (t) => {
    const path = './';
    const files = [{
        name: '.readify.js',
        size: '3.46kb',
        date: '12.01.2017',
        owner: 'root',
        mode: 'rw- rw- r--',
    }, {
        name: 'readdir.js',
        size: '1.59kb',
        date: '12.01.2017',
        owner: 'root',
        mode: 'rw- rw- r--',
    }];

    const str = stringify({
        path,
        files,
    });

    const stream = Readable.from(str);

    assign(stream, {
        contentLength: Buffer.byteLength(str),
        type: 'directory',
    });

    const read = stub().returns(stream);

    mockRequire('win32', {
        read,
    });

    reRequire('../server/fs/get');

    const restafary = reRequire('..');
    const {request} = serveOnce(restafary, {
        root: '/',
    });

    await request.get('/fs/bin?sort=name');

    const order = 'asc';
    const sort = 'name';
    const root = '/';

    t.calledWith(read, ['/bin', {sort, order, root}], 'should call readify with sort "name"');
    t.end();
});

test('restafary: get: sort by size', async (t) => {
    const path = '';
    const files = [];

    const str = stringify({
        path,
        files,
    });

    const stream = Readable.from(str);

    assign(stream, {
        type: 'directory',
        contentLength: Buffer.byteLength(str),
    });

    const read = stub().returns(stream);

    mockRequire('win32', {
        read,
    });

    reRequire('../server/fs/get');

    const restafary = reRequire('..');
    const {request} = serveOnce(restafary, {
        root: '/',
    });

    await request.get('/fs/bin?sort=size');

    const order = 'asc';
    const sort = 'size';
    const root = '/';

    t.calledWith(read, ['/bin', {sort, order, root}], 'should call readify with sort "size"');
    t.end();
});

test('restafary: get: sort by order', async (t) => {
    const path = '';
    const files = [];

    const str = stringify({
        path,
        files,
    });

    const stream = Readable.from(str);

    assign(stream, {
        contentLength: Buffer.byteLength(str),
        type: 'directory',
    });

    const read = stub().returns(stream);

    mockRequire('win32', {
        read,
    });

    reRequire('../server/fs/get');

    const restafary = reRequire('..');
    const {request} = serveOnce(restafary, {
        root: '/',
    });

    await request.get('/fs/bin?order=desc&sort=time');

    const sort = 'time';
    const order = 'desc';
    const root = '/';

    t.calledWith(read, ['/bin', {sort, order, root}], 'should call readify with sort and order');
    t.end();
});

test('restafary: path traversal: emoji', async (t) => {
    const url = encodeURI('/fs/fixture/🎉/');
    const {body} = await request.get(url, {
        type: 'json',
        options: {
            root: __dirname,
        },
    });

    delete body.files[0].owner;
    delete body.files[0].mode;
    delete body.files[0].date;

    const expected = {
        path: '/fixture/🎉/',
        files: [{
            name: 'hello.txt',
            size: '6b',
            type: 'file',
        }],
    };

    t.deepEqual(body, expected);
    t.end();
});

test('restafary: path traversal: zip', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip/', {
        options: {
            root: __dirname,
        },
    });

    const expected = stringify({
        path: '/fixture/dir.zip/',
        files: [{
            name: 'dir',
            size: '0b',
            date: '28.08.2017',
            mode: 'rw- rw- rw-',
            type: 'directory',
            owner: 'root',
        }],
    }, null, 4);

    t.equal(body, expected);
    t.end();
});

test('restafary: path traversal: zip: download', async (t) => {
    const {headers} = await request.get('/fs/fixture/get.zip/hello.txt?download', {
        options: {
            root: __dirname,
        },
    });

    const result = headers.get('Content-Disposition');

    t.equal(result, 'attachment');
    t.end();
});

test('restafary: get: content type', async (t) => {
    const {headers} = await request.get('/fs/fixture/index.html');
    const contentType = headers.get('Content-Type');
    const expected = 'text/html; charset=utf-8';

    t.equal(contentType, expected, 'should set content type');
    t.end();
});

test('restafary: get: content type: no extension', async (t) => {
    const {headers} = await request.get('/fs/fixture/image');
    const contentType = headers.get('Content-Type');
    const expected = 'image/x-icon';

    t.equal(contentType, expected, 'should set content type');
    t.end();
});

test('restafary: get: file: content length', async (t) => {
    const {headers} = await request.get('/fs/fixture/index.html');
    const contentType = headers.get('Content-Length');
    const expected = '14';

    t.equal(contentType, expected, 'should set content length');
    t.end();
});

test('restafary: get: dir: content length', async (t) => {
    const {headers} = await request.get('/fs/fixture/dir.zip');
    const contentType = headers.get('Content-Length');
    const expected = '232';

    t.equal(contentType, expected, 'should set content length');
    t.end();
});

test('restafary: zip size', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip/dir?size', {
        options: {
            root: __dirname,
        },
    });

    const expected = '6b';

    t.equal(body, expected);
    t.end();
});

test('restafary: outer size', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip?size', {
        options: {
            root: __dirname,
        },
    });

    const expected = '232b';

    t.equal(body, expected);
    t.end();
});

test('restafary: zip: hash', async (t) => {
    const {body} = await request.get('/fs/fixture/dir.zip/dir?hash', {
        options: {
            root: __dirname,
        },
    });

    const {length} = 'ce5a6d585669be2da407f6b2616128b3bc755c62';

    t.equal(body.length, length);
    t.end();
});

test('restafary: zip: hash: content length', async (t) => {
    const {headers} = await request.get('/fs/fixture/dir.zip/dir?hash', {
        options: {
            root: __dirname,
        },
    });

    const result = headers.get('content-length');
    const expected = '40';

    t.equal(result, expected);
    t.end();
});

<|endoftext|>


<commit_before>var test = require('tape');
var Quilt = require('../quilt');
var Backbone = require('backbone');
Backbone.$ = require('jquery/dist/jquery')(window);

test('data-ref creates a reference.', function(t) {
  var view = new Quilt.View;
  var p = $('

')[0];
  Quilt.patches.ref.call(view, p, 'p');
  t.ok(view.$p[0] === p);
  t.end();
});
<commit_msg>Updated ref test.<commit_after>var test = require('tape');
var Quilt = require('../quilt');
var Backbone = require('backbone');
Backbone.$ = require('jquery/dist/jquery')(window);

test('data-ref creates a reference.', function(t) {
  var view = new Quilt.View({
    template: function() {
      return '

';
    }
  }).render();
  t.ok(view.$test[0] === view.$('p')[0]);
  t.end();
});
<|endoftext|>


<commit_before>var assert = require('chai').assert;

var nJwt = require('../');

var properties = require('../properties.json');

var fs = require('fs');
var path = require('path');

var pair = {
  public: fs.readFileSync(path.join(__dirname,'rsa.pub'),'utf8'),
  private: fs.readFileSync(path.join(__dirname,'rsa.priv'),'utf8')
};

describe('JWT Builder',function(){
  describe('when RS256 is specified',function(){
    var token = new nJwt.Jwt({}).signWith('RS256',pair.private).compact();
    it('should create the token with the appropriate header values',function(){
      assert.isNotNull(token);
    });
  });
});

describe('parseClaimsJws',function() {

  describe('with a token that is signed with an RSA private key',function(){
    var claims = {foo:'bar'};
    var token = new nJwt.Jwt(claims).signWith('RS256',pair.private).compact();

    describe('and verified with the public key',function(){

      var result;

      before(function(done){
        new nJwt.Parser().setSigningKey('RS256',pair.public).parseClaimsJws(token,function(err,res){
          result = [err,res];
          done();
        });
      });

      it('should validate and return the token payload',function(){
        assert.isNull(result[0],'An unexpected error was returned');
        assert.isObject(result[1],'A result was not returned');
        assert.equal(result[1].body.foo,claims.foo);
      });
    });

    /*

    // The crypto library is throwing errors when i try to verify with
    // the private key, why?

    describe('and verified with the private key',function(){

      var result;

      before(function(done){
        new nJwt.Parser().setSigningKey('RS256',pair.private).parseClaimsJws(token,function(err,res){
          result = [err,res];
          done();
        });
      });

      it('should validate and return the token payload',function(){
        assert.isNull(result[0],'An unexpected error was returned');
        assert.isObject(result[1],'A result was not returned');
        assert.equal(result[1].body.foo,claims.foo);
      });
    });

    */
  });

  describe('with a token that is signed with an RSA public key',function(){

    var token = new nJwt.Jwt({foo:'bar'}).signWith('HS256',pair.public).compact();

    describe('and verified with the public key and the header is maliciously set to HS256',function(){

      var result;

      before(function(done){
        new nJwt.Parser().setSigningKey('RS256',pair.public).parseClaimsJws(token,function(err,res){
          result = [err,res];
          done();
        });
      });

      it('should return a signature mismatch error',function(){
        assert.isNotNull(result[0],'An error was not returned');
        assert.equal(result[0].userMessage,properties.errors.SIGNATURE_ALGORITHM_MISMTACH);
      });
    });

  });
});

<commit_msg>re-wording these tests<commit_after>var assert = require('chai').assert;

var nJwt = require('../');

var properties = require('../properties.json');

var fs = require('fs');
var path = require('path');

var pair = {
  public: fs.readFileSync(path.join(__dirname,'rsa.pub'),'utf8'),
  private: fs.readFileSync(path.join(__dirname,'rsa.priv'),'utf8')
};

describe('JWT Builder',function(){
  describe('when RS256 is specified',function(){
    var token = new nJwt.Jwt({}).signWith('RS256',pair.private).compact();
    it('should create the token with the appropriate header values',function(){
      assert.isNotNull(token);
    });
  });
});

describe('a token that is signed with an RSA private key',function() {

  var claims = {foo:'bar'};
  var token = new nJwt.Jwt(claims).signWith('RS256',pair.private).compact();

  describe('and a parser that is configurd with the RSA public key',function(){

    var parser = new nJwt.Parser().setSigningKey('RS256',pair.public);

    var result;

    before(function(done){
      parser.parseClaimsJws(token,function(err,res){
        result = [err,res];
        done();
      });
    });

    it('should validate and return the token payload',function(){
      assert.isNull(result[0],'An unexpected error was returned');
      assert.isObject(result[1],'A result was not returned');
      assert.equal(result[1].body.foo,claims.foo);
    });
  });

  // describe('and a parser that is configurd with the RSA private key',function(){

  //   // The crypto library is throwing errors when i try to verify with
  //   // the private key, why?

  //   var parser = new nJwt.Parser().setSigningKey('RS256',pair.private);

  //   var result;

  //   before(function(done){
  //     parser.parseClaimsJws(token,function(err,res){
  //       result = [err,res];
  //       done();
  //     });
  //   });

  //   it('should validate and return the token payload',function(){
  //     assert.isNull(result[0],'An unexpected error was returned');
  //     assert.isObject(result[1],'A result was not returned');
  //     assert.equal(result[1].body.foo,claims.foo);
  //   });
  // });

});

describe('a token that is signed with an RSA public key but header alg of HS256',function(){

  var token = new nJwt.Jwt({foo:'bar'}).signWith('HS256',pair.public).compact();

  describe('and a parser configured with RS256 and the same public key for vefification',function(){

    var parser = new nJwt.Parser().setSigningKey('RS256',pair.public);

    var result;

    before(function(done){
      parser.parseClaimsJws(token,function(err,res){
        result = [err,res];
        done();
      });
    });

    it('should return SIGNATURE_ALGORITHM_MISMTACH error',function(){
      assert.isNotNull(result[0],'An error was not returned');
      assert.equal(result[0].message,properties.errors.SIGNATURE_ALGORITHM_MISMTACH);
    });
  });

});
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2015 Benjamin Cordier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var Interactor = function (config) {
	this.__init__(config);
};

Interactor.prototype = {

	// Initialization
	__init__: function (config) {
		// Argument Assignment / Sanity Checks
		this.interactions 		= typeof(config.interactions) 				== "boolean" 	? config.interations 		: true,
		this.interactionElement = typeof(config.interactionElement) 		== "string" 	? config.interactionElement :'interaction',
		this.interactionEvents 	= Array.isArray(config.interactionEvents) 	== true 		? config.interactionEvents 	: ['mouseup', 'touchend'],
		this.conversions 		= typeof(config.coversions)					== "boolean" 	? config.conversions		: false,
		this.conversionElement 	= typeof(config.conversionElement) 			== "string" 	? config.conversionElement 	: 'conversion',
		this.conversionEvents 	= Array.isArray(config.conversionEvents) 	== true 		? config.conversionEvents 	: ['mouseup', 'touchend'],
		this.endpoint 			= typeof(config.endpoint) 					== "string" 	? config.endpoint 			: '/interactions',
		this.async 				= typeof(config.async) 						== "boolean" 	? config.async 				: true,
		this.records 			= [];
		this.loadTime 			= new Date();
		// Bind Events
		this.__bindEvents__();
	},

	// Create Events to Track
	__bindEvents__: function () {
		var interactor 	= this;

		// Set interactor Capture
		if (interactor.interactions === true) {
			for (var i = 0; i < interactor.interactionEvents.length; i++) {
				var ev 		= interactor.interactionEvents[i],
					targets = document.getElementsByClassName(interactor.interactionElement);
				for (var j = 0; j < targets.length; j++) {
					targets[j].addEventListener(ev, function (e) {
						e.stopPropagation();
						interactor.__addInteraction__(e, "interaction");
					});
				}
			}
		}

		// Set Conversion Capture
		if (interactor.conversions === true) {
			for (var i = 0; i < interactor.conversionEvents.length; i++) {
				var ev 		= interactor.events[i],
					targets = document.getElementsByClassName(interactor.conversionElement);
				for (var j = 0; j < targets.length; j++) {
					targets[j].addEventListener(ev, function (e) {
						e.stopPropagation();
						interactor.__addInteraction__(e, "conversion");
					});
				}
			}
		}

		// Bind onbeforeunload Event
		window.onbeforeunload = function (e) {
			interactor.__sendInteractions__();
		};
		return this;
	},

	// Add interactor Triggered By Events
	__addInteraction__: function (e, type) {
		var interactor 	= this,
			interaction 	= {
				type 			: type,
				event 			: e.type,
				targetTag 		: e.path[0].tagName,
				targetClasses 	: e.path[0].className,
				content 		: e.path[0].innerText,
				clientPosition  : {
					x 				: e.clientX,
					y 				: e.clientY
				},
				screenPosition 	: {
					x 				: e.screenX,
					y 				: e.screenY
				},
				createdAt 		: new Date()
			};
		interactor.records.push(interaction);
		return this;
	},

	// Gather additional data and send interaction(s) to server
	__sendInteractions__: function () {
		var interactor 	= this,
			data 			= {
				loadTime 		: interactor.loadTime,
				unloadTime 		: new Date(),
				language 		: window.navigator.language,
				platform 		: window.navigator.platform,
				port 			: window.location.port,
				client 			: {
					name 			: window.navigator.appVersion,
					innerWidth 		: window.innerWidth,
					innerHeight 	: window.innerHeight,
					outerWidth 		: window.outerWidth,
					outerHeight 	: window.outerHeight
				},
				page 			: {
					location 		: window.location.pathname,
					href 			: window.location.href,
					origin 			: window.location.origin,
					title 			: document.title
				},
				interactions 	: interactor.records
			},
			ajax  			= new XMLHttpRequest();
		ajax.open('POST', interactor.endpoint, interactor.async);
		ajax.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
		ajax.send(JSON.stringify(data));
	}
};<commit_msg>Version 1.2 - Minor update<commit_after>/*
The MIT License (MIT)

Copyright (c) 2015 Benjamin Cordier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var Interactor = function (config) {
	this.__init__(config);
};

Interactor.prototype = {

	// Initialization
	__init__: function (config) {
		// Argument Assignment / Sanity Checks
		this.interactions 		= typeof(config.interactions) 				== "boolean" 	? config.interations 		: true,
		this.interactionElement = typeof(config.interactionElement) 		== "string" 	? config.interactionElement :'interaction',
		this.interactionEvents 	= Array.isArray(config.interactionEvents) 	=== true 		? config.interactionEvents 	: ['mouseup', 'touchend'],
		this.conversions 		= typeof(config.coversions)					== "boolean" 	? config.conversions		: false,
		this.conversionElement 	= typeof(config.conversionElement) 			== "string" 	? config.conversionElement 	: 'conversion',
		this.conversionEvents 	= Array.isArray(config.conversionEvents) 	=== true 		? config.conversionEvents 	: ['mouseup', 'touchend'],
		this.endpoint 			= typeof(config.endpoint) 					== "string" 	? config.endpoint 			: '/interactions',
		this.async 				= typeof(config.async) 						== "boolean" 	? config.async 				: true,
		this.records 			= [];
		this.loadTime 			= new Date();
		// Bind Events
		this.__bindEvents__();
	},

	// Create Events to Track
	__bindEvents__: function () {
		var interactor 	= this;

		// Set interactor Capture
		if (interactor.interactions === true) {
			for (var i = 0; i < interactor.interactionEvents.length; i++) {
				var ev 		= interactor.interactionEvents[i],
					targets = document.getElementsByClassName(interactor.interactionElement);
				for (var j = 0; j < targets.length; j++) {
					targets[j].addEventListener(ev, function (e) {
						e.stopPropagation();
						interactor.__addInteraction__(e, "interaction");
					});
				}
			}
		}

		// Set Conversion Capture
		if (interactor.conversions === true) {
			for (var i = 0; i < interactor.conversionEvents.length; i++) {
				var ev 		= interactor.events[i],
					targets = document.getElementsByClassName(interactor.conversionElement);
				for (var j = 0; j < targets.length; j++) {
					targets[j].addEventListener(ev, function (e) {
						e.stopPropagation();
						interactor.__addInteraction__(e, "conversion");
					});
				}
			}
		}

		// Bind onbeforeunload Event
		window.onbeforeunload = function (e) {
			interactor.__sendInteractions__();
		};
		return this;
	},

	// Add interactor Triggered By Events
	__addInteraction__: function (e, type) {
		var interactor 	= this,
			interaction 	= {
				type 			: type,
				event 			: e.type,
				targetTag 		: e.path[0].tagName,
				targetClasses 	: e.path[0].className,
				content 		: e.path[0].innerText,
				clientPosition  : {
					x 				: e.clientX,
					y 				: e.clientY
				},
				screenPosition 	: {
					x 				: e.screenX,
					y 				: e.screenY
				},
				createdAt 		: new Date()
			};
		interactor.records.push(interaction);
		return this;
	},

	// Gather additional data and send interaction(s) to server
	__sendInteractions__: function () {
		var interactor 	= this,
			data 			= {
				loadTime 		: interactor.loadTime,
				unloadTime 		: new Date(),
				language 		: window.navigator.language,
				platform 		: window.navigator.platform,
				port 			: window.location.port,
				client 			: {
					name 			: window.navigator.appVersion,
					innerWidth 		: window.innerWidth,
					innerHeight 	: window.innerHeight,
					outerWidth 		: window.outerWidth,
					outerHeight 	: window.outerHeight
				},
				page 			: {
					location 		: window.location.pathname,
					href 			: window.location.href,
					origin 			: window.location.origin,
					title 			: document.title
				},
				interactions 	: interactor.records
			},
			xhr  			= new XMLHttpRequest();
		xhr.open('POST', interactor.endpoint, interactor.async);
		xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
		xhr.send(JSON.stringify(data));
	}
};<|endoftext|>


<commit_before>import { StyleSheet } from 'react-native';

export default StyleSheet.create({
  container: {
    flex: 1,
    paddingVertical: 10,
  },

  resizeModeSection: {
    marginBottom: 10,
  },

  tintColorSection: {
    paddingLeft: 10,
  },
});
<commit_msg>Fixed an ugly style<commit_after>import { StyleSheet } from 'react-native';

export default StyleSheet.create({
  container: {
    flex: 1,
    paddingVertical: 10,
  },

  resizeModeSection: {
    paddingLeft: 10,
  },

  tintColorSection: {
    paddingLeft: 10,
  },
});
<|endoftext|>


<commit_before>/*

Note: At this time the plugin wrapper for this code is still under development.
The logic will work if carefully transplanted to other projects, but it can't yet
simply be applied to a selection by calling d3.textwrap(). That is the eventual goal,
though – stay tuned!

*/

(function() {

    // create the plugin method twice, both for regular use
    // and again for use inside the enter() selection
    d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function(bounds) {

        // save callee into a variable so we can continue to refer to it
        // as the function scope changes
        var selection = this;

        // extract wrap boundaries from any d3-selected rect
        // and apply them to the text calling this method
        // (this is a placeholder, functionality coming soon)
        function extract_bounds(bounds) {
            return bounds; // this doesn't actually do anything yet
        }

        // double check the input argument for the wrapping
        // boundaries to make sure it actually contains all
        // the information we'll need in order to wrap successfully
        function verify_bounds(bounds) {
            // if it's an associative array, make sure it has all the
            // necessary properties represented directly
            if(
                (typeof bounds == 'object') &&
                (bounds.x) &&
                (bounds.y) &&
                (bounds.dx) &&
                (bounds.dy)
            ) {
                // if that's the case, then the bounds are fine and we
                // can just return them
                return bounds;
            // if it's a numerically indexed array, assume it's a
            // d3-selected rect and try to extract the positions
            } else if(
                    // first try to make sure it's an array using Array.isArray
                    (
                        (typeof Array.isArray == 'function') &&
                        (Array.isArray(bounds))
                    ) ||
                    // but since Array.isArray isn't always supported, fall
                    // back to casting to the object to string when it's not
                    (Object.prototype.toString.call(bounds) === '[object Array]')
            ) {
                // once you're sure it's an array, extract the boundaries
                // from the rect
                return extract_bounds(bounds);
            } else {
            // but if the bounds are neither an object nor a numerical
            // array, then the bounds argument is invalid and you'll
            // need to fix it
                return false;
            }
        }

        // check that we have the necessary conditions for this function to operate properly
        if(
            // selection it's operating on cannot be not empty
            (selection.length == 0) ||
            // d3 is available
            (!d3) ||
            // make sure this function has not already been declared
            (typeof d3.selection.prototype.textwrap !== 'undefined') ||
            // desired wrapping bounds must be provided as an input argument
            (!bounds) ||
            // input bounds must validate
            (!verify_bounds(bounds))
        ) {
            console.log('necessary conditions are not met; exiting without wrapping text');
            // try to return the calling selection if possible
            // so as not to interfere with methods downstream in the
            // chain
            if(selection) {
                return selection;
            // if all else fails, just return false. if you hit this point then you're
            // almost trying to call the textwrap() method on something that
            // doesn't make sense!
            } else {
                return false;
            }
        // if we've validated everything then we can finally proceed
        // to the meat of this operation
        } else {

            // wrap using html and foreignObjects if they are supported
            function wrap_with_foreign_objects(item) {
                console.log('wrapping with foreign object');
                // establish variables to quickly reference target nodes later
                var parent = d3.select(item.parentNode);
                var text_node = parent.selectAll('text');
                // extract our desired content from the single text element
                var text_to_wrap = text_node.text();
                // remove the text node and replace with a foreign object
                text_node.remove();
                var foreign_object = parent.append('foreignObject');
                // add foreign object and set dimensions, position, etc
                foreign_object
                    .attr("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility")
                    .attr('x', x_position)
                    .attr('y', y_position)
                    .attr('width', max_width)
                    .attr('height', max_height)
                ;
                // insert an HTML div
                var wrap_div = foreign_object
                    .append('xhtml:div')
                    // this class is currently hardcoded
                    // probably not necessary but easy to
                    // override using .classed() and for now
                    // it's nice to avoid a litany of input
                    // arguments
                    .attr('class', 'wrapped')
                ;
                // set div to same dimensions as foreign object
                wrap_div
                    .attr('height', max_height)
                    .attr('width', max_width)
                    .attr('style', 'padding:' + padding + 'px;')
                    // insert text content
                    .html(text_to_wrap)
                ;
            }

            // wrap with tspans if foreignObject is undefined
            function wrap_with_tspans(text_node) {
                console.log('wrapping with tspans');
                // only fire the rest of this if the text content
                // overflows the desired dimensions
                if(this.getBBox().width > max_width) {
                    // store whatever is inside the text node
                    // in a variable and then zero out the
                    // initial content; we'll reinsert in a moment
                    // using tspan elements.
                    var text_node = d3.select(this);
                    var text_to_wrap = text_node.text();
                    text_node.text('');

                    if(text_to_wrap) {
                        // split at spaces to create an array of individual words
                        var text_to_wrap_array;
                        if(text_to_wrap.indexOf(' ') !== -1) {
                            text_to_wrap_array = text_to_wrap.split(' ');
                        } else {
                            // if there are no spaces, chop it in half.
                            // this is a hack! better to apply
                            // CSS word-break: break-all in order
                            // to handle long single-word strings that
                            // might overflow without spaces. will fix to
                            // dynamically compute the appropriate number
                            // of cuts later.
                            var string_length = text_to_wrap.length;
                            var midpoint = parseInt(string_length / 2);
                            var first_half = text_to_wrap.substr(0, midpoint);
                            var second_half = text_to_wrap.substr(midpoint, string_length);
                            text_to_wrap_array = [first_half, second_half];
                        }

                        // new array where we'll store the words re-assembled into
                        // substrings that have been tested against the desired
                        // maximum wrapping width
                        var substrings = [];
                        // computed text length is arguably incorrectly reported for
                        // all tspans after the first one, in that they will include
                        // the width of previous separate tspans. to compensate we need
                        // to manually track the computed text length of all those
                        // previous tspans and substrings, and then use that to offset
                        // the miscalculation. this then gives us the actual correct
                        // position we want to use in rendering the text in the SVG.
                        var offset = 0;
                        // loop through the words and test the computed text length
                        // of the string against the maximum desired wrapping width
                        for(var i = 0; i < text_to_wrap_array.length; i++) {
                            var word = text_to_wrap_array[i];
                            var previous_string = text_node.text();
                            var previous_width = this.getComputedTextLength();
                            // initialize the current word as the first word
                            // or append to the previous string if one exists
                            var new_string;
                            if(previous_string) {
                                new_string = previous_string + ' ' + word;
                            } else {
                                new_string = word;
                            }
                            // add the newest substring back to the text node and
                            // measure the length
                            text_node.text(new_string);
                            var new_width = this.getComputedTextLength();
                            // adjust the length by the offset we've tracked
                            // due to the misreported length discussed above
                            var test_width = new_width - offset;
                            // if our latest version of the string is too
                            // big for the bounds, use the previous
                            // version of the string (without the newest word
                            // added) and use the latest word to restart the
                            // process with a new tspan
                            if(test_width > max_width) {
                                if(previous_string !== '') {
                                    var temp = {string: previous_string, width: previous_width - offset};
                                    substrings.push(temp);
                                    offset = offset + previous_width;
                                    text_node.text('');
                                    text_node.text(word);
                                }
                            }
                            // if we're up to the last word in the array,
                            // get the computed length as is without
                            // appending anything
                            if(i == text_to_wrap_array.length - 1) {
                                var final_string = new_string.substr(previous_string.length);
                                // remove leading spaces if present
                                if(final_string.substr(0, 1) == ' ') {
                                    final_string = final_string.substr(1);
                                }
                                if(final_string !== '') {
                                    if((new_width - offset) > 0) {new_width = new_width - offset}
                                    var temp = {string: final_string, width: new_width};
                                    substrings.push(temp);
                                    text_node.text('');
                                }
                            }
                        }
                        // double check that there are no empty substrings
                        // because those would create blank tspans
                        var substrings_clean = [];
                        for(var i = 0; i < substrings.length; i++) {
                            if(substrings[i].string.length > 0) {
                                substrings_clean.push(substrings[i]);
                            }
                        }
                        // append each substring as a tspan
                        var current_tspan;
                        var tspan_count;
                        for(var i = 0; i < substrings_clean.length; i++) {
                            var substring = substrings_clean[i].string;
                            if(i > 0) {
                                var previous_substring = substrings_clean[i - 1];
                            }
                            current_tspan = text_node.append('tspan')
                                .text(substring)
                            ;
                            current_tspan
                                .attr('dy', function(d) {
                                    if(i > 0) {
                                        // line height shouldn't be hard coded
                                        // will fix this later
                                        return '1.5em';
                                    }
                                })
                            ;
                            // shift left from default position, which
                            // is probably based on the full length of the
                            // text string until we make this adjustment
                            current_tspan
                                .attr('dx', function() {
                                    var render_offset = 0;
                                    if(i > 0) {
                                        render_offset = 0;
                                        for(j = 0; j < i; j++) {
                                            render_offset += substrings[j].width;
                                        }
                                    }
                                    return render_offset * -1;
                                })
                            ;
                        }
                    }
                }
            }

            // test for browser support and then switch
            // between the different text wrapping methods
            var wrap_method;
            if(typeof SVGForeignObjectElement !== 'undefined') {
                wrap_method = wrap_with_foreign_objects;
            } else {
                wrap_method = wrap_with_tspans;
            }

            // run the wrap function for each item
            // in the selection
            for(var i = 0; i < selection.length; i++) {
                var item = selection[i];
                wrap_method(item);
            }

        }

	}

})();
<commit_msg>comments<commit_after>/*

Note: At this time the plugin wrapper for this code is still under development.
The logic will work if carefully transplanted to other projects, but it can't yet
simply be applied to a selection by calling d3.textwrap(). That is the eventual goal,
though – stay tuned!

*/

(function() {

    // create the plugin method twice, both for regular use
    // and again for use inside the enter() selection
    d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function(bounds) {

        // save callee into a variable so we can continue to refer to it
        // as the function scope changes
        var selection = this;

        // extract wrap boundaries from any d3-selected rect
        // and apply them to the text calling this method
        // (this is a placeholder, functionality coming soon)
        function extract_bounds(bounds) {
            return bounds; // this doesn't actually do anything yet
        }

        // double check the input argument for the wrapping
        // boundaries to make sure it actually contains all
        // the information we'll need in order to wrap successfully
        function verify_bounds(bounds) {
            // if it's an associative array, make sure it has all the
            // necessary properties represented directly
            if(
                (typeof bounds == 'object') &&
                (bounds.x) &&
                (bounds.y) &&
                (bounds.dx) &&
                (bounds.dy)
            ) {
                // if that's the case, then the bounds are fine and we
                // can just return them
                return bounds;
            // if it's a numerically indexed array, assume it's a
            // d3-selected rect and try to extract the positions
            } else if(
                    // first try to make sure it's an array using Array.isArray
                    (
                        (typeof Array.isArray == 'function') &&
                        (Array.isArray(bounds))
                    ) ||
                    // but since Array.isArray isn't always supported, fall
                    // back to casting to the object to string when it's not
                    (Object.prototype.toString.call(bounds) === '[object Array]')
            ) {
                // once you're sure it's an array, extract the boundaries
                // from the rect
                return extract_bounds(bounds);
            } else {
            // but if the bounds are neither an object nor a numerical
            // array, then the bounds argument is invalid and you'll
            // need to fix it
                return false;
            }
        }

        // check that we have the necessary conditions for this function to operate properly
        if(
            // selection it's operating on cannot be not empty
            (selection.length == 0) ||
            // d3 must be available
            (!d3) ||
            // make sure this function has not already been declared
            (typeof d3.selection.prototype.textwrap !== 'undefined') ||
            // desired wrapping bounds must be provided as an input argument
            (!bounds) ||
            // input bounds must validate
            (!verify_bounds(bounds))
        ) {
            console.log('necessary conditions are not met; exiting without wrapping text');
            // try to return the calling selection if possible
            // so as not to interfere with methods downstream in the
            // chain
            if(selection) {
                return selection;
            // if all else fails, just return false. if you hit this point then you're
            // almost trying to call the textwrap() method on something that
            // doesn't make sense!
            } else {
                return false;
            }
        // if we've validated everything then we can finally proceed
        // to the meat of this operation
        } else {

            // wrap using html and foreignObjects if they are supported
            function wrap_with_foreign_objects(item) {
                console.log('wrapping with foreign object');
                // establish variables to quickly reference target nodes later
                var parent = d3.select(item.parentNode);
                var text_node = parent.selectAll('text');
                // extract our desired content from the single text element
                var text_to_wrap = text_node.text();
                // remove the text node and replace with a foreign object
                text_node.remove();
                var foreign_object = parent.append('foreignObject');
                // add foreign object and set dimensions, position, etc
                foreign_object
                    .attr("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility")
                    .attr('x', x_position)
                    .attr('y', y_position)
                    .attr('width', max_width)
                    .attr('height', max_height)
                ;
                // insert an HTML div
                var wrap_div = foreign_object
                    .append('xhtml:div')
                    // this class is currently hardcoded
                    // probably not necessary but easy to
                    // override using .classed() and for now
                    // it's nice to avoid a litany of input
                    // arguments
                    .attr('class', 'wrapped')
                ;
                // set div to same dimensions as foreign object
                wrap_div
                    .attr('height', max_height)
                    .attr('width', max_width)
                    .attr('style', 'padding:' + padding + 'px;')
                    // insert text content
                    .html(text_to_wrap)
                ;
            }

            // wrap with tspans if foreignObject is undefined
            function wrap_with_tspans(text_node) {
                console.log('wrapping with tspans');
                // only fire the rest of this if the text content
                // overflows the desired dimensions
                if(this.getBBox().width > max_width) {
                    // store whatever is inside the text node
                    // in a variable and then zero out the
                    // initial content; we'll reinsert in a moment
                    // using tspan elements.
                    var text_node = d3.select(this);
                    var text_to_wrap = text_node.text();
                    text_node.text('');

                    if(text_to_wrap) {
                        // split at spaces to create an array of individual words
                        var text_to_wrap_array;
                        if(text_to_wrap.indexOf(' ') !== -1) {
                            text_to_wrap_array = text_to_wrap.split(' ');
                        } else {
                            // if there are no spaces, chop it in half.
                            // this is a hack! better to apply
                            // CSS word-break: break-all in order
                            // to handle long single-word strings that
                            // might overflow without spaces. will fix to
                            // dynamically compute the appropriate number
                            // of cuts later.
                            var string_length = text_to_wrap.length;
                            var midpoint = parseInt(string_length / 2);
                            var first_half = text_to_wrap.substr(0, midpoint);
                            var second_half = text_to_wrap.substr(midpoint, string_length);
                            text_to_wrap_array = [first_half, second_half];
                        }

                        // new array where we'll store the words re-assembled into
                        // substrings that have been tested against the desired
                        // maximum wrapping width
                        var substrings = [];
                        // computed text length is arguably incorrectly reported for
                        // all tspans after the first one, in that they will include
                        // the width of previous separate tspans. to compensate we need
                        // to manually track the computed text length of all those
                        // previous tspans and substrings, and then use that to offset
                        // the miscalculation. this then gives us the actual correct
                        // position we want to use in rendering the text in the SVG.
                        var offset = 0;
                        // loop through the words and test the computed text length
                        // of the string against the maximum desired wrapping width
                        for(var i = 0; i < text_to_wrap_array.length; i++) {
                            var word = text_to_wrap_array[i];
                            var previous_string = text_node.text();
                            var previous_width = this.getComputedTextLength();
                            // initialize the current word as the first word
                            // or append to the previous string if one exists
                            var new_string;
                            if(previous_string) {
                                new_string = previous_string + ' ' + word;
                            } else {
                                new_string = word;
                            }
                            // add the newest substring back to the text node and
                            // measure the length
                            text_node.text(new_string);
                            var new_width = this.getComputedTextLength();
                            // adjust the length by the offset we've tracked
                            // due to the misreported length discussed above
                            var test_width = new_width - offset;
                            // if our latest version of the string is too
                            // big for the bounds, use the previous
                            // version of the string (without the newest word
                            // added) and use the latest word to restart the
                            // process with a new tspan
                            if(test_width > max_width) {
                                if(previous_string !== '') {
                                    var temp = {string: previous_string, width: previous_width - offset};
                                    substrings.push(temp);
                                    offset = offset + previous_width;
                                    text_node.text('');
                                    text_node.text(word);
                                }
                            }
                            // if we're up to the last word in the array,
                            // get the computed length as is without
                            // appending anything
                            if(i == text_to_wrap_array.length - 1) {
                                var final_string = new_string.substr(previous_string.length);
                                // remove leading spaces if present
                                if(final_string.substr(0, 1) == ' ') {
                                    final_string = final_string.substr(1);
                                }
                                if(final_string !== '') {
                                    if((new_width - offset) > 0) {new_width = new_width - offset}
                                    var temp = {string: final_string, width: new_width};
                                    substrings.push(temp);
                                    text_node.text('');
                                }
                            }
                        }
                        // double check that there are no empty substrings
                        // because those would create blank tspans
                        var substrings_clean = [];
                        for(var i = 0; i < substrings.length; i++) {
                            if(substrings[i].string.length > 0) {
                                substrings_clean.push(substrings[i]);
                            }
                        }
                        // append each substring as a tspan
                        var current_tspan;
                        var tspan_count;
                        for(var i = 0; i < substrings_clean.length; i++) {
                            var substring = substrings_clean[i].string;
                            if(i > 0) {
                                var previous_substring = substrings_clean[i - 1];
                            }
                            current_tspan = text_node.append('tspan')
                                .text(substring)
                            ;
                            current_tspan
                                .attr('dy', function(d) {
                                    if(i > 0) {
                                        // line height shouldn't be hard coded
                                        // will fix this later
                                        return '1.5em';
                                    }
                                })
                            ;
                            // shift left from default position, which
                            // is probably based on the full length of the
                            // text string until we make this adjustment
                            current_tspan
                                .attr('dx', function() {
                                    var render_offset = 0;
                                    if(i > 0) {
                                        render_offset = 0;
                                        for(j = 0; j < i; j++) {
                                            render_offset += substrings[j].width;
                                        }
                                    }
                                    return render_offset * -1;
                                })
                            ;
                        }
                    }
                }
            }

            // test for browser support and then switch
            // between the different text wrapping methods
            var wrap_method;
            if(typeof SVGForeignObjectElement !== 'undefined') {
                wrap_method = wrap_with_foreign_objects;
            } else {
                wrap_method = wrap_with_tspans;
            }

            // run the wrap function for each item
            // in the selection
            for(var i = 0; i < selection.length; i++) {
                var item = selection[i];
                wrap_method(item);
            }

        }

	}

})();
<|endoftext|>


<commit_before>;(function(Backbone) {
'use strict';

var THIS_ID = '(this)';

Backbone.Cord.mediaQueries = {
	all: '',
	hd: 'only screen and (max-width: 1200px)',
	desktop: 'only screen and (max-width: 992px)',
	tablet: 'only screen and (max-width: 768px)',
	phablet: 'only screen and (max-width: 480px)',
	mobile: 'only screen and (max-width: 320px)'
};

var ua = navigator.userAgent.toLowerCase();
var browser = (/(chrome|safari)/.exec(ua) || /firefox/.exec(ua) || /msie/.exec(ua) || /opera/.exec(ua) || '')[0];
var stylePrefix = ({ chrome: 'webkit', firefox: 'Moz', msie: 'ms', opera: 'O', safari: 'webkit' })[browser] || '';
var cssPrefix = '-' + stylePrefix.toLowerCase() + '-';

function _createStyleSheets() {
	var el, key;
	Backbone.Cord._styleSheets = {};
	for(key in Backbone.Cord.mediaQueries) {
		if(Backbone.Cord.mediaQueries.hasOwnProperty(key)) {
			// Note: cannot use id on stlye tags, but could add a data attribute for identifying
			// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
			// https://davidwalsh.name/add-rules-stylesheets
			el = document.createElement('style');
			el.type = 'text/css';
			if(Backbone.Cord.mediaQueries[key])
				el.media = Backbone.Cord.mediaQueries[key];
			// Webkit hack
			el.appendChild(document.createTextNode(''));
			document.head.appendChild(el);
			Backbone.Cord._styleSheets[key] = el.sheet;
		}
	}
}

function _getStylePrefix(style, css) {
	if(document.documentElement.style[style] === void(0))
		return css ? cssPrefix : stylePrefix;
	return '';
}

function _camelCaseToDash(str) {
	var i, c, start = 0;
	var words = [];
	for(i = 0; i < str.length; ++i) {
		c = str.charCodeAt(i);
		if(c >= 65 && c <= 90 && i) {
			words.push(str.substring(start, i).toLowerCase());
			start = i;
		}
	}
	words.push(str.substring(start, i).toLowerCase());
	return words.join('-');
}

function _addRules(rules, _styles, selector, media, id) {
	var key, sheet, mediaQuery, idQuery;
	media = media || 'all';
	sheet = Backbone.Cord._styleSheets[media];
	for(key in rules) {
		if(rules.hasOwnProperty(key)) {
			if(typeof rules[key] === 'object') {
				mediaQuery = idQuery = null;
				if(key.indexOf(Backbone.Cord.config.mediaPrefix) === 0) {
					mediaQuery = key.substr(Backbone.Cord.config.mediaPrefix.length);
					if(!Backbone.Cord.mediaQueries[mediaQuery])
						return;
				}
				if(!mediaQuery && Backbone.Cord.mediaQueries[key])
					mediaQuery = key;
				if(!mediaQuery) {
					if(key[0] === '_' || key[0] === '#')
						idQuery = key.substr(1);
					if(idQuery && !Backbone.Cord.regex.testIdProperty(idQuery, true))
						idQuery = null;
				}
				if(mediaQuery)
					_addRules(rules[key], _styles, selector, mediaQuery);
				else if(idQuery)
					_addRules(rules[key], _styles, selector + '>' + Backbone.Cord.regex.replaceIdSelectors('#' + idQuery), media, idQuery);
				else
					_addRules(rules[key], _styles, selector + '>' + Backbone.Cord.regex.replaceIdSelectors(key), media);
			}
			else {
				if(rules[key].search(Backbone.Cord.regex.variableSearch) !== -1) {
					var scope = id || THIS_ID;
					if(!_styles[scope])
						_styles[scope] = {};
					_styles[scope][key] = rules[key];
				}
				else {
					var rule = selector + '{' + _getStylePrefix(key, true) + _camelCaseToDash(key) + ':' + rules[key] + ';}';
					Backbone.Cord.log('@' + media,  rule);
					sheet.insertRule(rule, 0);
				}
			}
		}
	}
}

var __extend = Backbone.Cord.View.extend;
Backbone.Cord.View.extend = function(properties) {
	// Look for styles hash
	var _styles = {};
	if(properties.styles && properties.className) {
		if(!Backbone.Cord._styleSheets)
			_createStyleSheets();
		_addRules(properties.styles, _styles, '.' + properties.className.split(' ').join('.'));
	}
	var View = __extend.apply(this, arguments);
	View.prototype._styles = _styles;
	return View;
};

function _createStyleObserver(node, style) {
	style = _getStylePrefix(style) + style;
	return function(key, formatted) {
		node.style[style] = Backbone.Cord.convertToString(formatted);
	};
}

function _styles(context, attrs) {
	var styles = attrs.style;
	if(styles) {
		if(typeof styles === 'string' && context.isView && this[styles]) {
			styles = this[styles];
		}
		if(typeof styles === 'object') {
			// The math plugin doesn't do a deep process of the attributes so invoke string processing here
			this._plugin('strings', context, styles);
			for(var style in styles) {
				if(styles.hasOwnProperty(style)) {
					if(styles[style].match(Backbone.Cord.regex.variableSearch) && context.isView)
						this.observeFormat(styles[style], _createStyleObserver(context.el, style), true);
					else
						context.el.style[_getStylePrefix(style) + style] = styles[style];
				}
			}
			delete attrs.style;
		}
	}
}

// Accept a style that is either an object or a key to a style object on this (non-binding!)
// e.g. {style: 'mystyle'}, where this.mystyle is an object
// To create an inline style bound to a property, use the binding plugin
// Otherwise, a normal inline-styles string will fall-through and get applied outside this plugin
// e.g. background-color: green; cursor: help;
// When interpolating variables into styles, use this over an interpolated attribute string value
// because the interpolated string value will overwrite any changes to styles on the node made through javascript e.g. the hidden plugin
// Note: interpolated styles are not supported under media queries and require idProperties to be true
Backbone.Cord.plugins.push({
	name: 'styles',
	requirements: ['interpolation'],
	config: {
		mediaPrefix: '@'
	},
	attrs: _styles,
	bindings: _styles,
	initialize: function(context) {
		if(this._styles && this._styles[THIS_ID]) {
			var styles = Backbone.Cord.copyObj(this._styles[THIS_ID]);
			Backbone.Cord.log(styles);
			this._plugin('strings', context, styles);
			for(var style in styles) {
				if(styles.hasOwnProperty(style))
					this.observeFormat(styles[style], _createStyleObserver(this.el, style), true);
			}
		}
	},
	complete: function(context) {
		// Apply any dynamic class styles detected from the initial extend
		if(this._styles && context.id && this._styles[context.id]) {
			var styles = Backbone.Cord.copyObj(this._styles[context.id]);
			Backbone.Cord.log(styles);
			this._plugin('strings', context, styles);
			for(var style in styles) {
				if(styles.hasOwnProperty(style))
					this.observeFormat(styles[style], _createStyleObserver(context.el, style), true);
			}
		}
	}
});

})(((typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global)).Backbone || require('backbone'));
<commit_msg>Added trident when detecting browser prefixes for styles.<commit_after>;(function(Backbone) {
'use strict';

var THIS_ID = '(this)';

Backbone.Cord.mediaQueries = {
	all: '',
	hd: 'only screen and (max-width: 1200px)',
	desktop: 'only screen and (max-width: 992px)',
	tablet: 'only screen and (max-width: 768px)',
	phablet: 'only screen and (max-width: 480px)',
	mobile: 'only screen and (max-width: 320px)'
};

var ua = navigator.userAgent.toLowerCase();
var browser = (/(chrome|safari)/.exec(ua) || /firefox/.exec(ua) || /msie/.exec(ua) || /trident/.exec(ua) || /opera/.exec(ua) || '')[0];
var stylePrefix = ({ chrome: 'webkit', firefox: 'Moz', msie: 'ms', opera: 'O', safari: 'webkit', trident: 'ms' })[browser] || '';
var cssPrefix = '-' + stylePrefix.toLowerCase() + '-';

function _createStyleSheets() {
	var el, key;
	Backbone.Cord._styleSheets = {};
	for(key in Backbone.Cord.mediaQueries) {
		if(Backbone.Cord.mediaQueries.hasOwnProperty(key)) {
			// Note: cannot use id on stlye tags, but could add a data attribute for identifying
			// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
			// https://davidwalsh.name/add-rules-stylesheets
			el = document.createElement('style');
			el.type = 'text/css';
			if(Backbone.Cord.mediaQueries[key])
				el.media = Backbone.Cord.mediaQueries[key];
			// Webkit hack
			el.appendChild(document.createTextNode(''));
			document.head.appendChild(el);
			Backbone.Cord._styleSheets[key] = el.sheet;
		}
	}
}

function _getStylePrefix(style, css) {
	if(document.documentElement.style[style] === void(0))
		return css ? cssPrefix : stylePrefix;
	return '';
}

function _camelCaseToDash(str) {
	var i, c, start = 0;
	var words = [];
	for(i = 0; i < str.length; ++i) {
		c = str.charCodeAt(i);
		if(c >= 65 && c <= 90 && i) {
			words.push(str.substring(start, i).toLowerCase());
			start = i;
		}
	}
	words.push(str.substring(start, i).toLowerCase());
	return words.join('-');
}

function _addRules(rules, _styles, selector, media, id) {
	var key, sheet, mediaQuery, idQuery;
	media = media || 'all';
	sheet = Backbone.Cord._styleSheets[media];
	for(key in rules) {
		if(rules.hasOwnProperty(key)) {
			if(typeof rules[key] === 'object') {
				mediaQuery = idQuery = null;
				if(key.indexOf(Backbone.Cord.config.mediaPrefix) === 0) {
					mediaQuery = key.substr(Backbone.Cord.config.mediaPrefix.length);
					if(!Backbone.Cord.mediaQueries[mediaQuery])
						return;
				}
				if(!mediaQuery && Backbone.Cord.mediaQueries[key])
					mediaQuery = key;
				if(!mediaQuery) {
					if(key[0] === '_' || key[0] === '#')
						idQuery = key.substr(1);
					if(idQuery && !Backbone.Cord.regex.testIdProperty(idQuery, true))
						idQuery = null;
				}
				if(mediaQuery)
					_addRules(rules[key], _styles, selector, mediaQuery);
				else if(idQuery)
					_addRules(rules[key], _styles, selector + '>' + Backbone.Cord.regex.replaceIdSelectors('#' + idQuery), media, idQuery);
				else
					_addRules(rules[key], _styles, selector + '>' + Backbone.Cord.regex.replaceIdSelectors(key), media);
			}
			else {
				if(rules[key].search(Backbone.Cord.regex.variableSearch) !== -1) {
					var scope = id || THIS_ID;
					if(!_styles[scope])
						_styles[scope] = {};
					_styles[scope][key] = rules[key];
				}
				else {
					var rule = selector + '{' + _getStylePrefix(key, true) + _camelCaseToDash(key) + ':' + rules[key] + ';}';
					Backbone.Cord.log('@' + media,  rule);
					sheet.insertRule(rule, 0);
				}
			}
		}
	}
}

var __extend = Backbone.Cord.View.extend;
Backbone.Cord.View.extend = function(properties) {
	// Look for styles hash
	var _styles = {};
	if(properties.styles && properties.className) {
		if(!Backbone.Cord._styleSheets)
			_createStyleSheets();
		_addRules(properties.styles, _styles, '.' + properties.className.split(' ').join('.'));
	}
	var View = __extend.apply(this, arguments);
	View.prototype._styles = _styles;
	return View;
};

function _createStyleObserver(node, style) {
	style = _getStylePrefix(style) + style;
	return function(key, formatted) {
		node.style[style] = Backbone.Cord.convertToString(formatted);
	};
}

function _styles(context, attrs) {
	var styles = attrs.style;
	if(styles) {
		if(typeof styles === 'string' && context.isView && this[styles]) {
			styles = this[styles];
		}
		if(typeof styles === 'object') {
			// The math plugin doesn't do a deep process of the attributes so invoke string processing here
			this._plugin('strings', context, styles);
			for(var style in styles) {
				if(styles.hasOwnProperty(style)) {
					if(styles[style].match(Backbone.Cord.regex.variableSearch) && context.isView)
						this.observeFormat(styles[style], _createStyleObserver(context.el, style), true);
					else
						context.el.style[_getStylePrefix(style) + style] = styles[style];
				}
			}
			delete attrs.style;
		}
	}
}

// Accept a style that is either an object or a key to a style object on this (non-binding!)
// e.g. {style: 'mystyle'}, where this.mystyle is an object
// To create an inline style bound to a property, use the binding plugin
// Otherwise, a normal inline-styles string will fall-through and get applied outside this plugin
// e.g. background-color: green; cursor: help;
// When interpolating variables into styles, use this over an interpolated attribute string value
// because the interpolated string value will overwrite any changes to styles on the node made through javascript e.g. the hidden plugin
// Note: interpolated styles are not supported under media queries and require idProperties to be true
Backbone.Cord.plugins.push({
	name: 'styles',
	requirements: ['interpolation'],
	config: {
		mediaPrefix: '@'
	},
	attrs: _styles,
	bindings: _styles,
	initialize: function(context) {
		if(this._styles && this._styles[THIS_ID]) {
			var styles = Backbone.Cord.copyObj(this._styles[THIS_ID]);
			Backbone.Cord.log(styles);
			this._plugin('strings', context, styles);
			for(var style in styles) {
				if(styles.hasOwnProperty(style))
					this.observeFormat(styles[style], _createStyleObserver(this.el, style), true);
			}
		}
	},
	complete: function(context) {
		// Apply any dynamic class styles detected from the initial extend
		if(this._styles && context.id && this._styles[context.id]) {
			var styles = Backbone.Cord.copyObj(this._styles[context.id]);
			Backbone.Cord.log(styles);
			this._plugin('strings', context, styles);
			for(var style in styles) {
				if(styles.hasOwnProperty(style))
					this.observeFormat(styles[style], _createStyleObserver(context.el, style), true);
			}
		}
	}
});

})(((typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global)).Backbone || require('backbone'));
<|endoftext|>