<commit_before>'use strict'

var co = require('co')
var test = require('tape').test
var tlog = require('./helpers/testLog')
var Fly = require('../lib/fly')
var cli = require('../lib/cli')
var pkg = require('../package')
var join = require('path').join
var arrify = require('arrify')

var fixtures = join(process.cwd(), 'test', 'fixtures')
var flypath = join(fixtures, 'flyfile.js')

test('✈  cli', function (t) {
	t.ok(cli !== undefined, 'is defined')
	arrify('help', 'list', 'spawn', 'version').forEach(function (command) {
		t.equal(command in cli, true, command + ' is defined')
	})
	t.end()
})

test('✈  cli.version', function (t) {
	tlog.call(t, function () {
		cli.version(pkg)
	}, function (actual) {
		t.equal(actual, pkg.name + ', ' + pkg.version, 'log fly version')
	})
	t.end()
})

test('✈  cli.help', function (t) {
	tlog.call(t, cli.help, function (actual) {
		t.equal(actual, '\n\x1b[2m\x1b[1mUsage\x1b[0m\n  fly [options] [tasks]\n\n\x1b[2m\x1b[1mOptions\x1b[0m\n  \x1b[2m\x1b[1m-\x1b[0m\x1b[1mh \x1b[0m\x1b[2m\x1b[1m --\x1b[0m\x1b[1mhelp\x1b[0m     Display this help.\n  \x1b[2m\x1b[1m-\x1b[0m\x1b[1mf \x1b[0m\x1b[2m\x1b[1m --\x1b[0m\x1b[1mfile\x1b[0m     Use an alternate Flyfile.\n  \x1b[2m\x1b[1m-\x1b[0m\x1b[1ml \x1b[0m\x1b[2m\x1b[1m --\x1b[0m\x1b[1mlist\x1b[0m     Display available tasks.\n  \x1b[2m\x1b[1m-\x1b[0m\x1b[1mv \x1b[0m\x1b[2m\x1b[1m --\x1b[0m\x1b[1mversion\x1b[0m  Display version.\n  ',
			'show fly help')
	})
	t.end()
})

test('✈  cli.list', function (t) {
	var message = 'list tasks in a fly instance'
	arrify(true, false).forEach(function (bare) {
		tlog.call(t, function () {
			cli.list(require(flypath), {bare: bare})
		}, function (actual, key) {
			if (!bare && actual === '\n\x1b[2m\x1b[1mAvailable tasks\x1b[0m') {
				return true
			}
			if (key === 'a' || key === 'b') {
				return true
			} else if (key === 'c') {
				t.ok(true, 'message / bare ' + bare)
			} else {
				t.ok(false, message)
			}
		})
	})
	t.end()
})

test('✈  cli.spawn', function (t) {
	t.plan(6)

	var alt = join('test', 'fixtures', 'alt')
	var file = join('test', 'fixtures', 'alt', 'flyfile.js')

	Array.prototype.concat(alt, file).map(function (fpath) {
		return ({
			flypath: fpath,
			spawn: co.wrap(cli.spawn)(fpath)
		})
	}).forEach(function (_) {
		_.spawn.then(function (fly) {
			t.ok(fly instanceof Fly && fly.host && Array.isArray(fly.plugins),
				'spawn a fly instance using ' + _.flypath)

			t.ok(fly.plugins.length === 1 && fly.fakePlugin,
				'load plugins from package.json')

			fly.filter = function (f) {
				t.equal('ylf', f.cb('fly'),
					'add plugin function to fly instance and get expected result')
			}

			fly.fakePlugin()
		}).catch(function (e) {
			t.ok(false, e)
		})
	})
})
<commit_msg>fix cli.help test<commit_after>'use strict'

var co = require('co')
var test = require('tape').test
var tlog = require('./helpers/testLog')
var Fly = require('../lib/fly')
var cli = require('../lib/cli')
var pkg = require('../package')
var join = require('path').join
var arrify = require('arrify')

var fixtures = join(process.cwd(), 'test', 'fixtures')
var flypath = join(fixtures, 'flyfile.js')

test('✈  cli', function (t) {
	t.ok(cli !== undefined, 'is defined')
	arrify('help', 'list', 'spawn', 'version').forEach(function (command) {
		t.equal(command in cli, true, command + ' is defined')
	})
	t.end()
})

test('✈  cli.version', function (t) {
	tlog.call(t, function () {
		cli.version(pkg)
	}, function (actual) {
		t.equal(actual, pkg.name + ', ' + pkg.version, 'log fly version')
	})
	t.end()
})

test('✈  cli.help', t => {
	tlog.call(t, cli.help, actual => {
		t.true(/Options/gm.test(actual), 'show fly help');
	});
	t.end();
});

test('✈  cli.list', function (t) {
	var message = 'list tasks in a fly instance'
	arrify(true, false).forEach(function (bare) {
		tlog.call(t, function () {
			cli.list(require(flypath), {bare: bare})
		}, function (actual, key) {
			if (!bare && actual === '\n\x1b[2m\x1b[1mAvailable tasks\x1b[0m') {
				return true
			}
			if (key === 'a' || key === 'b') {
				return true
			} else if (key === 'c') {
				t.ok(true, 'message / bare ' + bare)
			} else {
				t.ok(false, message)
			}
		})
	})
	t.end()
})

test('✈  cli.spawn', function (t) {
	t.plan(6)

	var alt = join('test', 'fixtures', 'alt')
	var file = join('test', 'fixtures', 'alt', 'flyfile.js')

	Array.prototype.concat(alt, file).map(function (fpath) {
		return ({
			flypath: fpath,
			spawn: co.wrap(cli.spawn)(fpath)
		})
	}).forEach(function (_) {
		_.spawn.then(function (fly) {
			t.ok(fly instanceof Fly && fly.host && Array.isArray(fly.plugins),
				'spawn a fly instance using ' + _.flypath)

			t.ok(fly.plugins.length === 1 && fly.fakePlugin,
				'load plugins from package.json')

			fly.filter = function (f) {
				t.equal('ylf', f.cb('fly'),
					'add plugin function to fly instance and get expected result')
			}

			fly.fakePlugin()
		}).catch(function (e) {
			t.ok(false, e)
		})
	})
})
<|endoftext|>


<commit_before>"use strict";

const assert = require('assert');
const xpath = require('fontoxpath');
const utils = require('./utils');

const mei = utils.getTestMeiDom('barnumbers.mei');

describe("bar numbers", function() {
    it("exports @n attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure/@n", mei);
        assert.deepEqual(nAttributes, ['1', '2', '3', '4'], "@n is simply ascending");
    });
    it("exports @label attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure", mei).map(m => m.getAttribute('label'));
        assert.deepEqual(nAttributes, [null, null, '5', '1'], "@n is simply ascending");
    });
});
<commit_msg>Fix test description in barnumbers test<commit_after>"use strict";

const assert = require('assert');
const xpath = require('fontoxpath');
const utils = require('./utils');

const mei = utils.getTestMeiDom('barnumbers.mei');

describe("bar numbers", function() {
    it("exports @n attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure/@n", mei);
        assert.deepEqual(nAttributes, ['1', '2', '3', '4'], "@n is simply ascending");
    });
    it("exports @label attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure", mei).map(m => m.getAttribute('label'));
        assert.deepEqual(nAttributes, [null, null, '5', '1'], "@label represents displayed bar numbers");
    });
});
<|endoftext|>


<commit_before>/* global require describe it */
var U = require('lodash');
var expect = require('expect.js');
var BLOXX  = require('../lib/bloxx');
var characterFixture = require('./fixtures/character-1009151');

describe('Model initalization', function () {
	'use strict';

	var Registry = BLOXX.Registry.create();

	var Character = Registry.createModel({
		type: 'Character',
		defaults: {
			name: null,
			description: null,
			modified: null,
			urls: []
		}
	});

	describe('when record is new', function () {
		var attrs = U.cloneDeep(characterFixture);
		delete attrs.id;
		var character = Character.create(attrs);

		it('does not have an .id', function () {
			expect(character.id).to.be(null);
			expect(character.isNew()).to.be(true);
		});

		it('has a .name', function () {
			expect(character.name).to.be('Amiko');
		});

		it('has nested .urls', function () {
			var url = character.urls[0];
			expect(url.type).to.be('detail');
			expect(typeof url.url).to.be('string');
		});
	});

	describe('when record has an id', function () {
		var character = Character.create(characterFixture);

		it('has an .id', function () {
			expect(character.id).to.be('1009151');
			expect(character.isNew()).to.be(false);
		});

		it('has a .name', function () {
			expect(character.name).to.be('Amiko');
		});

		it('has nested .urls', function () {
			var url = character.urls[0];
			expect(url.type).to.be('detail');
			expect(typeof url.url).to.be('string');
		});
	});

	describe('without relationships', function () {
		var Character = Registry.createModel({
			type: 'Character',
			defaults: {
				name: null,
				description: null,
				modified: null,
				thumbnail: null,
				resourceURI: null,
				urls: []
			}
		});

		var Comic = Registry.createModel({
			type: 'Comic',
			defaults: {}
		});

		var character = Character.create(characterFixture);
		var comic = character.relationships.belongsTo.Comic[0];

		it('relationships are not model instances', function () {
			expect(comic).not.to.be.a(Comic);
			expect(comic.save).to.be(undefined);
		});
	});

	describe('with relationships', function () {
		var Character = Registry.createModel({
			type: 'Character',
			defaults: {
				name: null,
				description: null,
				modified: null,
				thumbnail: null,
				resourceURI: null,
				urls: []
			},
			relationshipDefinitions: 'belongsToComic'
		});

		var Comic = Registry.createModel({
			type: 'Comic',
			defaults: {
				title: null,
				pageCount: null
			},
			relationshipDefinitions: 'belongsToCreator hasCharacter'
		});

		Registry.createModel({
			type: 'Creator',
			defaults: {
				firstName: null,
				lastName: null,
				thumbnail: null
			},
			relationshipDefinitions: 'hasComic'
		});

		var character = Character.create(characterFixture);
		var comic = character.relationships.belongsTo.Comic[0];

		it('relationships are Comic instances', function () {
			expect(comic).to.be.a(Comic);
			expect(comic.save).to.be.a(Function);
		});
	});
});
<commit_msg>Delint a test file.<commit_after>/* global require describe it */
var U = require('lodash');
var expect = require('expect.js');
var BLOXX = require('../lib/bloxx');
var characterFixture = require('./fixtures/character-1009151');

describe('Model initalization', function () {
	'use strict';

	var Registry = BLOXX.Registry.create();

	var Character = Registry.createModel({
		type: 'Character',
		defaults: {
			name: null,
			description: null,
			modified: null,
			urls: []
		}
	});

	describe('when record is new', function () {
		var attrs = U.cloneDeep(characterFixture);
		delete attrs.id;
		var character = Character.create(attrs);

		it('does not have an .id', function () {
			expect(character.id).to.be(null);
			expect(character.isNew()).to.be(true);
		});

		it('has a .name', function () {
			expect(character.name).to.be('Amiko');
		});

		it('has nested .urls', function () {
			var url = character.urls[0];
			expect(url.type).to.be('detail');
			expect(typeof url.url).to.be('string');
		});
	});

	describe('when record has an id', function () {
		var character = Character.create(characterFixture);

		it('has an .id', function () {
			expect(character.id).to.be('1009151');
			expect(character.isNew()).to.be(false);
		});

		it('has a .name', function () {
			expect(character.name).to.be('Amiko');
		});

		it('has nested .urls', function () {
			var url = character.urls[0];
			expect(url.type).to.be('detail');
			expect(typeof url.url).to.be('string');
		});
	});

	describe('without relationships', function () {
		var Character = Registry.createModel({
			type: 'Character',
			defaults: {
				name: null,
				description: null,
				modified: null,
				thumbnail: null,
				resourceURI: null,
				urls: []
			}
		});

		var Comic = Registry.createModel({
			type: 'Comic',
			defaults: {}
		});

		var character = Character.create(characterFixture);
		var comic = character.relationships.belongsTo.Comic[0];

		it('relationships are not model instances', function () {
			expect(comic).not.to.be.a(Comic);
			expect(comic.save).to.be(undefined);
		});
	});

	describe('with relationships', function () {
		var Character = Registry.createModel({
			type: 'Character',
			defaults: {
				name: null,
				description: null,
				modified: null,
				thumbnail: null,
				resourceURI: null,
				urls: []
			},
			relationshipDefinitions: 'belongsToComic'
		});

		var Comic = Registry.createModel({
			type: 'Comic',
			defaults: {
				title: null,
				pageCount: null
			},
			relationshipDefinitions: 'belongsToCreator hasCharacter'
		});

		Registry.createModel({
			type: 'Creator',
			defaults: {
				firstName: null,
				lastName: null,
				thumbnail: null
			},
			relationshipDefinitions: 'hasComic'
		});

		var character = Character.create(characterFixture);
		var comic = character.relationships.belongsTo.Comic[0];

		it('relationships are Comic instances', function () {
			expect(comic).to.be.a(Comic);
			expect(comic.save).to.be.a(Function);
		});
	});
});
<|endoftext|>


<commit_before>'use strict';

var assert  = require('chai').assert;
var express = require('express');
var path    = require('path');
var _       = require('lodash');
var navit   = require('../');

describe('Navit.set.*', function () {
  var server;
  var browser;

  before(function (done) {
    browser = navit({ prefix: 'http://localhost:17345', engine: process.env.ENGINE });

    server = express()
        .use(express.static(path.join(__dirname, '..')))
        .get('/test/fixtures/set/headers.html', function (req, res) {
          res.send(JSON.stringify(req.headers));
        })
        .listen(17345, function (err) {

      if (err) {
        done(err);
        return;
      }

      // Init phantom before execute first test
      browser.run(done);
    });
  });

  // TODO: authentication test

  it('useragent', function (done) {
    browser
      .set.useragent('test-ua')
      .open('/test/fixtures/set/useragent.html')
      .test.evaluate(function () {
        return window.navigator.userAgent === 'test-ua';
      })
      .run(function (err) {
        done(err);
      });
  });

  it('zoom', function (done) {
    browser
      .open('/test/fixtures/set/zoom.html')
      .set.zoom(0.5)
      .test.evaluate(function () {
        return window.screen.width / window.innerWidth <= 0.5;
      })
      .set.zoom(1)
      .run(function (err) {
        done(err);
      });
  });

  it('viewport', function (done) {
    browser
      .open('/test/fixtures/set/viewport.html')
      .set.viewport(110, 120)
      .test.evaluate(function () {
        return window.innerWidth === 110 && window.innerHeight === 120;
      })
      .run(function (err) {
        done(err);
      });
  });

  it('cookies', function (done) {
    browser
      .open('/test/fixtures/set/cookies.html')
      .set.cookie({
        name: 'test',
        value: 'cookie',
        path: '/test'
      })
      .get.cookies(function (cookies) {
        var cookie = _.find(cookies, function (cookie) {
          return cookie.name === 'test';
        });

        assert.equal(cookie.value, 'cookie');
      })
      // Remove cookie
      .set.cookie({
        name: 'test',
        path: '/test',
        value: 'cookie',
        expires: Date.now() - 1000
      })
      .get.cookies(function (cookies) {
        assert.equal(cookies.length, 0);
      })
      .run(done);
  });

  it('headers', function (done) {
    browser
      .set.headers({ 'test-header': 'test-value' })
      .open('/test/fixtures/set/headers.html')
      .test.body(/test-value/)
      .run(done);
  });

  after(function () {
    server.close();
    browser.close();
  });
});
<commit_msg>Fixed zoom & viewport tests<commit_after>'use strict';

var assert  = require('chai').assert;
var express = require('express');
var path    = require('path');
var _       = require('lodash');
var navit   = require('../');

describe('Navit.set.*', function () {
  var server;
  var browser;

  before(function (done) {
    browser = navit({ prefix: 'http://localhost:17345', engine: process.env.ENGINE });

    server = express()
        .use(express.static(path.join(__dirname, '..')))
        .get('/test/fixtures/set/headers.html', function (req, res) {
          res.send(JSON.stringify(req.headers));
        })
        .listen(17345, function (err) {

      if (err) {
        done(err);
        return;
      }

      // Init phantom before execute first test
      browser.run(done);
    });
  });

  // TODO: authentication test

  it('useragent', function (done) {
    browser
      .set.useragent('test-ua')
      .open('/test/fixtures/set/useragent.html')
      .test.evaluate(function () {
        return window.navigator.userAgent === 'test-ua';
      })
      .run(function (err) {
        done(err);
      });
  });

  it('zoom', function (done) {
    var size;

    browser
      .open('/test/fixtures/set/zoom.html')
      .get.evaluate(function () {
        return [ window.innerWidth, window.innerHeight ];
      }, function (data) {
        size = data;
      })
      .set.zoom(0.5)
      .get.evaluate(function () {
        return [ window.innerWidth, window.innerHeight ];
      }, function (data) {
        assert.deepEqual(data, [ size[0] * 2, size[1] * 2 ]);
      })
      .set.zoom(1)
      .run(function (err) {
        done(err);
      });
  });

  it('viewport', function (done) {
    browser
      .open('/test/fixtures/set/viewport.html')
      .set.zoom(1)
      .set.viewport(300, 400)
      .get.evaluate(function () {
        return [ window.innerWidth, window.innerHeight ];
      }, function (data) {
        assert.deepEqual(data, [ 300, 400 ]);
      })
      .set.viewport(110, 128)
      .get.evaluate(function () {
        return [ window.innerWidth, window.innerHeight ];
      }, function (data) {
        assert.deepEqual(data, [ 110, 128 ]);
      })
      .run(function (err) {
        done(err);
      });
  });

  it('cookies', function (done) {
    browser
      .open('/test/fixtures/set/cookies.html')
      .set.cookie({
        name: 'test',
        value: 'cookie',
        path: '/test'
      })
      .get.cookies(function (cookies) {
        var cookie = _.find(cookies, function (cookie) {
          return cookie.name === 'test';
        });

        assert.equal(cookie.value, 'cookie');
      })
      // Remove cookie
      .set.cookie({
        name: 'test',
        path: '/test',
        value: 'cookie',
        expires: Date.now() - 1000
      })
      .get.cookies(function (cookies) {
        assert.equal(cookies.length, 0);
      })
      .run(done);
  });

  it('headers', function (done) {
    browser
      .set.headers({ 'test-header': 'test-value' })
      .open('/test/fixtures/set/headers.html')
      .test.body(/test-value/)
      .run(done);
  });

  after(function () {
    server.close();
    browser.close();
  });
});
<|endoftext|>


<commit_before>var tape = require("tape"),
    shape = require("../../");

tape("stackOrderAppearance(series) returns an order by appearance", function(test) {
  test.deepEqual(shape.stackOrderAppearance([
    [[0, 0], [0, 0], [0, 0]],
    [[0, 0], [0, 2], [0, 3]],
    [[0, 0], [0, 0], [0, 4]]
  ]), [1, 2, 0]);
  test.end();
});

tape("stackOrderAppearance(series) treats NaN values as zero", function(test) {
  test.deepEqual(shape.stackOrderAppearance([
    [[0, 0], [0, NaN], [0, NaN]],
    [[0, 0], [0, 2], [0, 3]],
    [[0, 0], [0, NaN], [0, 4]]
  ]), [1, 2, 0]);
  test.end();
});
<commit_msg>Fix tests.<commit_after>var tape = require("tape"),
    shape = require("../../");

tape("stackOrderAppearance(series) returns an order by appearance", function(test) {
  test.deepEqual(shape.stackOrderAppearance([
    [[0, 0], [0, 0], [0, 1]],
    [[0, 3], [0, 2], [0, 0]],
    [[0, 0], [0, 4], [0, 0]]
  ]), [1, 2, 0]);
  test.end();
});

tape("stackOrderAppearance(series) treats NaN values as zero", function(test) {
  test.deepEqual(shape.stackOrderAppearance([
    [[0, NaN], [0, NaN], [0, 1]],
    [[0, 3], [0, 2], [0, NaN]],
    [[0, NaN], [0, 4], [0, NaN]]
  ]), [1, 2, 0]);
  test.end();
});
<|endoftext|>


<commit_before>module.exports = function throttle(delay, fn) {
	if (arguments.length == 1) {
		fn = delay
		delay = 0
	}
	var throttleTimeout
	return function throttled() {
		if (throttleTimeout) { return }
		throttleTimeout = setTimeout(function() { throttleTimeout = null }, delay)
		fn.apply(this, arguments)
	}
}<commit_msg>improve throttle<commit_after>module.exports = function throttle(delay, fn) {
	if (arguments.length == 1) {
		fn = delay
		delay = 50
	}
	var lastCallTime
	return function throttled() {
		if (lastCallTime && (new Date().getTime() - lastCallTime) < delay) { return }
		lastCallTime = new Date().getTime()
		fn.apply(this, arguments)
	}
}<|endoftext|>


<commit_before>var JSHint = require('jshint').JSHINT;
var CSSLint = require('csslint');
var domify = require('domify');
var _ = require('lodash');

var ResponseTypes = {
	LINT : 'lint',
	ERROR : 'error',
	FAILURE : 'failure',
	SUCCESS : 'success'
};

var Response = function(type, message, errors){
	this.type = type;
	this.message = message || '';
	this.errors = errors;
};

var RinzaiError = function(message, line, char){
	this.message = message;
	this.line = line;
	this.char = char;
};

var Question = function(config, options){
	this.options = options;
	this.test = config.test;
	this.messages = config.messages || {};
	this.type = config.type;
};

var HTMLQuestion = function(){
	Question.apply(this, arguments);
};

HTMLQuestion.prototype.answer = function(content){
	var nodes;

	try {
		nodes = domify(content);
	} catch(e) {
		return new Response(
			ResponseTypes.ERROR,
			this.messages[ResponseTypes.ERROR],
			[
				new RinzaiError('HTML Parse Error', null, null)
			]
		);
	}

	try {
		this.test(nodes);
	} catch(e) {
		return new Response(
			ResponseTypes.FAILURE,
			this.messages[ResponseTypes.FAILURE],
			[
				new RinzaiError(e.message, null, null)
			]
		);
	}

	return new Response(
		ResponseTypes.SUCCESS,
		this.messages[ResponseTypes.SUCCESS]
	);
};

var JSQuestion = function(){
	Question.apply(this, arguments);
};

JSQuestion.prototype.ask = function(content){
	JSHint(content, this.options.jshint);
	if(JSHint.errors.length){
		var errors = [];
		JSHint.errors.forEach(function(err){
			errors.push(new RinzaiError(err.reason, err.line - 1, err.char - 1));
		});
		return new Response(
			ResponseTypes.LINT,
			this.messages[ResponseTypes.LINT],
			errors
		);
	}
	try {
		this.test(content);
	} catch(e){
		var firstStack = e.stack.split('\n')[1];
		if(firstStack.indexOf('eval') > -1){
			var position = firstStack.match(/(\d+)\:(\d+)\)$/);
			return new Response(
				ResponseTypes.ERROR,
				this.messages[ResponseTypes.ERROR],
				[
					new RinzaiError(e.message, parseInt(position[1], 10), parseInt(position[2], 10))
				]
			);
		} else {
			return new Response(
				ResponseTypes.FAILURE,
				this.messages[ResponseTypes.FAILURE],
				[
					new RinzaiError(e.message, null, null)
				]
			);
		}
	}
	return new Response(
		ResponseTypes.SUCCESS,
		this.messages[ResponseTypes.SUCCESS]
	);
};

var CSSQuestion = function(config){
	this.test = config.test;
};

CSSQuestion.prototype.ask = function(content){

};

var Rinzai = function(config, options){
	this.options = options || {};
	this.questions = [];
	this.questionsById = {};
	_.forEach(config.questions, this.addQuestion, this);
};

Rinzai.prototype.addQuestion = function(q){
	var question;
	switch(q.type){
		case 'html':
			question = new HTMLQuestion(q, this.options);
			break;
		case 'javascript':
			question = new JSQuestion(q, this.options);
			break;
		case 'css':
			question = new CSSQuestion(q, this.options);
			break;
		default:
			throw new Error('Question type is unknown or undefined');
	}
	this.questions.push(question);
	this.questionsById[q.id] = question;
	return question;
};

module.exports = Rinzai;
<commit_msg>normalized line numbers in errors<commit_after>var JSHint = require('jshint').JSHINT;
var CSSLint = require('csslint');
var domify = require('domify');
var _ = require('lodash');

var ResponseTypes = {
	LINT : 'lint',
	ERROR : 'error',
	FAILURE : 'failure',
	SUCCESS : 'success'
};

var Response = function(type, message, errors){
	this.type = type;
	this.message = message || '';
	this.errors = errors;
};

var RinzaiError = function(message, line, char){
	this.message = message;
	this.line = line;
	this.char = char;
};

var Question = function(config, options){
	this.options = options;
	this.test = config.test;
	this.messages = config.messages || {};
	this.type = config.type;
};

var HTMLQuestion = function(){
	Question.apply(this, arguments);
};

HTMLQuestion.prototype.answer = function(content){
	var nodes;

	try {
		nodes = domify(content);
	} catch(e) {
		return new Response(
			ResponseTypes.ERROR,
			this.messages[ResponseTypes.ERROR],
			[
				new RinzaiError('HTML Parse Error', null, null)
			]
		);
	}

	try {
		this.test(nodes);
	} catch(e) {
		return new Response(
			ResponseTypes.FAILURE,
			this.messages[ResponseTypes.FAILURE],
			[
				new RinzaiError(e.message, null, null)
			]
		);
	}

	return new Response(
		ResponseTypes.SUCCESS,
		this.messages[ResponseTypes.SUCCESS]
	);
};

var JSQuestion = function(){
	Question.apply(this, arguments);
};

JSQuestion.prototype.ask = function(content){
	JSHint(content, this.options.jshint);
	if(JSHint.errors.length){
		var errors = [];
		JSHint.errors.forEach(function(err){
			errors.push(new RinzaiError(err.reason, err.line));
		});
		return new Response(
			ResponseTypes.LINT,
			this.messages[ResponseTypes.LINT],
			errors
		);
	}
	try {
		this.test(content);
	} catch(e){
		var firstStack = e.stack.split('\n')[1];
		if(firstStack.indexOf('eval') > -1){
			var position = firstStack.match(/(\d+)\:(\d+)\)$/);
			return new Response(
				ResponseTypes.ERROR,
				this.messages[ResponseTypes.ERROR],
				[
					new RinzaiError(e.message, parseInt(position[1], 10), parseInt(position[2], 10))
				]
			);
		} else {
			return new Response(
				ResponseTypes.FAILURE,
				this.messages[ResponseTypes.FAILURE],
				[
					new RinzaiError(e.message, null, null)
				]
			);
		}
	}
	return new Response(
		ResponseTypes.SUCCESS,
		this.messages[ResponseTypes.SUCCESS]
	);
};

var CSSQuestion = function(config){
	this.test = config.test;
};

CSSQuestion.prototype.ask = function(content){

};

var Rinzai = function(config, options){
	this.options = options || {};
	this.questions = [];
	this.questionsById = {};
	_.forEach(config.questions, this.addQuestion, this);
};

Rinzai.prototype.addQuestion = function(q){
	var question;
	switch(q.type){
		case 'html':
			question = new HTMLQuestion(q, this.options);
			break;
		case 'javascript':
			question = new JSQuestion(q, this.options);
			break;
		case 'css':
			question = new CSSQuestion(q, this.options);
			break;
		default:
			throw new Error('Question type is unknown or undefined');
	}
	this.questions.push(question);
	this.questionsById[q.id] = question;
	return question;
};

module.exports = Rinzai;
<|endoftext|>


<commit_before>
UI.Component.instantiate = function (parent) {
  var kind = this;

  // check arguments
  if (UI.isComponent(kind)) {
    if (kind.isInited)
      throw new Error("A component kind is required, not an instance");
  } else {
    throw new Error("Expected Component kind");
  }

  var inst = kind.extend(); // XXX args go here
  inst.isInited = true;

  // XXX messy to define this here
  inst.templateInstance = {
    findAll: function (selector) {
      // XXX check that `.dom` exists here?
      return inst.dom.$(selector);
    },
    find: function (selector) {
      var result = this.findAll(selector);
      return result[0] || null;
    },
    firstNode: null,
    lastNode: null,
    data: null,
    __component__: inst
  };

  inst.parent = (parent || null);

  if (inst.init)
    inst.init();

  if (inst.created) {
    updateTemplateInstance(inst);
    inst.created.call(inst.templateInstance);
  }

  return inst;
};

UI.Component.render = function () {
  return null;
};

// Takes a reactive function (call it `inner`) and returns a reactive function
// `outer` which is equivalent except in its reactive behavior.  Specifically,
// `outer` has the following two special properties:
//
// 1. Isolation:  An invocation of `outer()` only invalidates its context
//    when the value of `inner()` changes.  For example, `inner` may be a
//    function that gets one or more Session variables and calculates a
//    true/false value.  `outer` blocks invalidation signals caused by the
//    Session variables changing and sends a signal out only when the value
//    changes between true and false (in this example).  The value can be
//    of any type, and it is compared with `===` unless an `equals` function
//    is provided.
//
// 2. Value Sharing:  The `outer` function returned by `emboxValue` can be
//    shared between different contexts, for example by assigning it to an
//    object as a method that can be accessed at any time, such as by
//    different templates or different parts of a template.  No matter
//    how many times `outer` is called, `inner` is only called once until
//    it changes.  The most recent value is stored internally.
//
// Conceptually, an emboxed value is much like a Session variable which is
// kept up to date by an autorun.  Session variables provide storage
// (value sharing) and they don't notify their listeners unless a value
// actually changes (isolation).  The biggest difference is that such an
// autorun would never be stopped, and the Session variable would never be
// deleted even if it wasn't used any more.  An emboxed value, on the other
// hand, automatically stops computing when it's not being used, and starts
// again when called from a reactive context.  This means that when it stops
// being used, it can be completely garbage-collected.
//
// If a non-function value is supplied to `emboxValue` instead of a reactive
// function, then `outer` is still a function but it simply returns the value.
//
UI.emboxValue = function (funcOrValue, equals) {
  if (typeof funcOrValue === 'function') {
    var func = funcOrValue;

    var curResult = null;
    // There's one shared Dependency and Computation for all callers of
    // our box function.  It gets kicked off if necessary, and when
    // there are no more dependents, it gets stopped to avoid leaking
    // memory.
    var resultDep = null;
    var computation = null;

    return function () {
      if (! computation) {
        if (! Deps.active) {
          // Not in a reactive context.  Just call func, and don't start a
          // computation if there isn't one running already.
          return func();
        }

        // No running computation, so kick one off.  Since this computation
        // will be shared, avoid any association with the current computation
        // by using `Deps.nonreactive`.
        resultDep = new Deps.Dependency;

        computation = Deps.nonreactive(function () {
          return Deps.autorun(function (c) {
            var oldResult = curResult;
            curResult = func();
            if (! c.firstRun) {
              if (! (equals ? equals(curResult, oldResult) :
                     curResult === oldResult))
                resultDep.changed();
            }
          });
        });
      }

      if (Deps.active) {
        var isNew = resultDep.depend();
        if (isNew) {
          // For each new dependent, schedule a task for after that dependent's
          // invalidation time and the subsequent flush. The task checks
          // whether the computation should be torn down.
          Deps.onInvalidate(function () {
            if (resultDep && ! resultDep.hasDependents()) {
              Deps.afterFlush(function () {
                // use a second afterFlush to bump ourselves to the END of the
                // flush, after computation re-runs have had a chance to
                // re-establish their connections to our computation.
                Deps.afterFlush(function () {
                  if (resultDep && ! resultDep.hasDependents()) {
                    computation.stop();
                    computation = null;
                    resultDep = null;
                  }
                });
              });
            }
          });
        }
      }

      return curResult;
    };

  } else {
    var value = funcOrValue;
    var result = function () {
      return value;
    };
    result._isEmboxedConstant = true;
    return result;
  }
};

////////////////////////////////////////

UI.insert = function (renderedTemplate, parentElement, nextNode) {
  if (! renderedTemplate.dom)
    throw new Error("Expected template rendered with UI.render");

  UI.DomRange.insert(renderedTemplate.dom, parentElement, nextNode);
};

// Insert a DOM node or DomRange into a DOM element or DomRange.
//
// One of three things happens depending on what needs to be inserted into what:
// - `range.add` (anything into DomRange)
// - `UI.DomRange.insert` (DomRange into element)
// - `elem.insertBefore` (node into element)
//
// The optional `before` argument is an existing node or id to insert before in
// the parent element or DomRange.
var insert = function (nodeOrRange, parent, before) {
  if (! parent)
    throw new Error("Materialization parent required");

  if (parent instanceof UI.DomRange) {
    parent.add(nodeOrRange, before);
  } else if (nodeOrRange instanceof UI.DomRange) {
    // parent is an element; inserting a range
    UI.DomRange.insert(nodeOrRange, parent, before);
  } else {
    // parent is an element; inserting an element
    parent.insertBefore(nodeOrRange, before || null); // `null` for IE
  }
};

// Update attributes on `elem` to the dictionary `attrs`, using the
// dictionary of existing `handlers` if provided.
//
// Values in the `attrs` dictionary are in pseudo-DOM form -- a string,
// CharRef, or array of strings and CharRefs -- but they are passed to
// the AttributeHandler in string form.
var updateAttributes = function(elem, newAttrs, handlers) {

  if (handlers) {
    for (var k in handlers) {
      if (! newAttrs.hasOwnProperty(k)) {
        // remove attributes (and handlers) for attribute names
        // that don't exist as keys of `newAttrs` and so won't
        // be visited when traversing it.  (Attributes that
        // exist in the `newAttrs` object but are `null`
        // are handled later.)
        var handler = handlers[k];
        var oldValue = handler.value;
        handler.value = null;
        handler.update(elem, oldValue, null);
        delete handlers[k];
      }
    }
  }

  for (var k in newAttrs) {
    var handler = null;
    var oldValue;
    var value = newAttrs[k];
    if ((! handlers) || (! handlers.hasOwnProperty(k))) {
      if (value !== null) {
        // make new handler
        handler = makeAttributeHandler(elem, k, value);
        if (handlers)
          handlers[k] = handler;
        oldValue = null;
      }
    } else {
      handler = handlers[k];
      oldValue = handler.value;
    }
    if (handler && oldValue !== value) {
      handler.value = value;
      handler.update(elem, oldValue, value);
      if (value === null)
        delete handlers[k];
    }
  }
};

UI.render = function (kind, parentComponent) {
  if (kind.isInited)
    throw new Error("Can't render component instance, only component kind");
  var inst = kind.instantiate(parentComponent);

  var content = (inst.render && inst.render());

  var range = new UI.DomRange;
  inst.dom = range;
  range.component = inst;

  materialize(content, range, null, inst);

  range.removed = function () {
    inst.isDestroyed = true;
    if (inst.destroyed) {
      updateTemplateInstance(inst);
      inst.destroyed.call(inst.templateInstance);
    }
  };

  return inst;
};

UI.renderWithData = function (kind, data, parentComponent) {
  if (! UI.isComponent(kind))
    throw new Error("Component required here");
  if (kind.isInited)
    throw new Error("Can't render component instance, only component kind");
  if (typeof data === 'function')
    throw new Error("Data argument can't be a function");

  return UI.render(kind.extend({data: function () { return data; }}),
                   parentComponent);
};

var contentEquals = function (a, b) {
  if (a instanceof HTML.Raw) {
    return (b instanceof HTML.Raw) && (a.value === b.value);
  } else if (a == null) {
    return (b == null);
  } else {
    return (a === b) &&
      ((typeof a === 'number') || (typeof a === 'boolean') ||
       (typeof a === 'string'));
  }
};

UI.InTemplateScope = function (tmplInstance, content) {
  if (! (this instanceof UI.InTemplateScope))
    // called without `new`
    return new UI.InTemplateScope(tmplInstance, content);

  var parentPtr = tmplInstance.parent;
  if (parentPtr.__isTemplateWith)
    parentPtr = parentPtr.parent;

  this.parentPtr = parentPtr;
  this.content = content;
};

UI.InTemplateScope.prototype.toHTML = function (parentComponent) {
  return HTML.toHTML(this.content, this.parentPtr);
};

UI.InTemplateScope.prototype.toText = function (textMode, parentComponent) {
  return HTML.toText(this.content, textMode, this.parentPtr);
};

// Convert the pseudoDOM `node` into reactive DOM nodes and insert them
// into the element or DomRange `parent`, before the node or id `before`.
var materialize = function (node, parent, before, parentComponent) {
  // XXX should do more error-checking for the case where user is supplying the tags.
  // For example, check that CharRef has `html` and `str` properties and no content.
  // Check that Comment has a single string child and no attributes.  Etc.

  if (node == null) {
    // null or undefined.
    // do nothinge.
  } else if ((typeof node === 'string') || (typeof node === 'boolean') || (typeof node === 'number')) {
    node = String(node);
    insert(document.createTextNode(node), parent, before);
  } else if (node instanceof Array) {
    for (var i = 0; i < node.length; i++)
      materialize(node[i], parent, before, parentComponent);
  } else if (typeof node === 'function') {

    var range = new UI.DomRange;
    var lastContent = null;
    var rangeUpdater = Deps.autorun(function (c) {
      var content = node();
      // normalize content a little, for easier comparison
      if (HTML.isNully(content))
        content = null;
      else if ((content instanceof Array) && content.length === 1)
        content = content[0];

      // update if content is different from last time
      if (! contentEquals(content, lastContent)) {
        lastContent = content;

        if (! c.firstRun)
          range.removeAll();

        Deps.nonreactive(function () {
          materialize(content, range, null, parentComponent);
        });
      }
    });
    range.removed = function () {
      rangeUpdater.stop();
    };
    insert(range, parent, before);
  } else if (node instanceof HTML.Tag) {
    var tagName = node.tagName;
    var elem;
    if (HTML.isKnownSVGElement(tagName) && document.createElementNS) {
      elem = document.createElementNS('http://www.w3.org/2000/svg', tagName);
    } else {
      elem = document.createElement(node.tagName);
    }

    var rawAttrs = node.attrs;
    var children = node.children;
    if (node.tagName === 'textarea') {
      rawAttrs = (rawAttrs || {});
      rawAttrs.value = children;
      children = [];
    };

    if (rawAttrs) {
      var attrUpdater = Deps.autorun(function (c) {
        if (! c.handlers)
          c.handlers = {};

        try {
          var attrs = HTML.evaluateAttributes(rawAttrs, parentComponent);
          var stringAttrs = {};
          if (attrs) {
            for (var k in attrs) {
              stringAttrs[k] = HTML.toText(attrs[k], HTML.TEXTMODE.STRING,
                                           parentComponent);
            }
            updateAttributes(elem, stringAttrs, c.handlers);
          }
        } catch (e) {
          reportUIException(e);
        }
      });
      UI.DomBackend.onRemoveElement(elem, function () {
        attrUpdater.stop();
      });
    }
    materialize(children, elem, null, parentComponent);

    insert(elem, parent, before);
  } else if (typeof node.instantiate === 'function') {
    // component
    var instance = UI.render(node, parentComponent);

    insert(instance.dom, parent, before);
  } else if (node instanceof HTML.CharRef) {
    insert(document.createTextNode(node.str), parent, before);
  } else if (node instanceof HTML.Comment) {
    insert(document.createComment(node.sanitizedValue), parent, before);
  } else if (node instanceof HTML.Raw) {
    // Get an array of DOM nodes by using the browser's HTML parser
    // (like innerHTML).
    var htmlNodes = UI.DomBackend.parseHTML(node.value);
    for (var i = 0; i < htmlNodes.length; i++)
      insert(htmlNodes[i], parent, before);
  } else if (Package['html-tools'] && (node instanceof Package['html-tools'].HTMLTools.Special)) {
    throw new Error("Can't materialize Special tag, it's just an intermediate rep");
  } else if (node instanceof UI.InTemplateScope) {
    materialize(node.content, parent, before, node.parentPtr);
  } else {
    // can't get here
    throw new Error("Unexpected node in htmljs: " + node);
  }
};

// XXX figure out the right names, and namespace, for these.
// for example, maybe some of them go in the HTML package.
UI.materialize = materialize;

UI.body = UI.Component.extend({
  kind: 'body',
  contentParts: [],
  render: function () {
    return this.contentParts;
  },
  // XXX revisit how body works.
  INSTANTIATED: false
});

UI.block = function (renderFunc) {
  return UI.Component.extend({ render: renderFunc });
};

UI.toHTML = function (content, parentComponent) {
  return HTML.toHTML(content, parentComponent);
};

UI.toRawText = function (content, parentComponent) {
  return HTML.toText(content, HTML.TEXTMODE.STRING, parentComponent);
};
<commit_msg>Nonreactive callback for range.removed code<commit_after>
UI.Component.instantiate = function (parent) {
  var kind = this;

  // check arguments
  if (UI.isComponent(kind)) {
    if (kind.isInited)
      throw new Error("A component kind is required, not an instance");
  } else {
    throw new Error("Expected Component kind");
  }

  var inst = kind.extend(); // XXX args go here
  inst.isInited = true;

  // XXX messy to define this here
  inst.templateInstance = {
    findAll: function (selector) {
      // XXX check that `.dom` exists here?
      return inst.dom.$(selector);
    },
    find: function (selector) {
      var result = this.findAll(selector);
      return result[0] || null;
    },
    firstNode: null,
    lastNode: null,
    data: null,
    __component__: inst
  };

  inst.parent = (parent || null);

  if (inst.init)
    inst.init();

  if (inst.created) {
    updateTemplateInstance(inst);
    inst.created.call(inst.templateInstance);
  }

  return inst;
};

UI.Component.render = function () {
  return null;
};

// Takes a reactive function (call it `inner`) and returns a reactive function
// `outer` which is equivalent except in its reactive behavior.  Specifically,
// `outer` has the following two special properties:
//
// 1. Isolation:  An invocation of `outer()` only invalidates its context
//    when the value of `inner()` changes.  For example, `inner` may be a
//    function that gets one or more Session variables and calculates a
//    true/false value.  `outer` blocks invalidation signals caused by the
//    Session variables changing and sends a signal out only when the value
//    changes between true and false (in this example).  The value can be
//    of any type, and it is compared with `===` unless an `equals` function
//    is provided.
//
// 2. Value Sharing:  The `outer` function returned by `emboxValue` can be
//    shared between different contexts, for example by assigning it to an
//    object as a method that can be accessed at any time, such as by
//    different templates or different parts of a template.  No matter
//    how many times `outer` is called, `inner` is only called once until
//    it changes.  The most recent value is stored internally.
//
// Conceptually, an emboxed value is much like a Session variable which is
// kept up to date by an autorun.  Session variables provide storage
// (value sharing) and they don't notify their listeners unless a value
// actually changes (isolation).  The biggest difference is that such an
// autorun would never be stopped, and the Session variable would never be
// deleted even if it wasn't used any more.  An emboxed value, on the other
// hand, automatically stops computing when it's not being used, and starts
// again when called from a reactive context.  This means that when it stops
// being used, it can be completely garbage-collected.
//
// If a non-function value is supplied to `emboxValue` instead of a reactive
// function, then `outer` is still a function but it simply returns the value.
//
UI.emboxValue = function (funcOrValue, equals) {
  if (typeof funcOrValue === 'function') {
    var func = funcOrValue;

    var curResult = null;
    // There's one shared Dependency and Computation for all callers of
    // our box function.  It gets kicked off if necessary, and when
    // there are no more dependents, it gets stopped to avoid leaking
    // memory.
    var resultDep = null;
    var computation = null;

    return function () {
      if (! computation) {
        if (! Deps.active) {
          // Not in a reactive context.  Just call func, and don't start a
          // computation if there isn't one running already.
          return func();
        }

        // No running computation, so kick one off.  Since this computation
        // will be shared, avoid any association with the current computation
        // by using `Deps.nonreactive`.
        resultDep = new Deps.Dependency;

        computation = Deps.nonreactive(function () {
          return Deps.autorun(function (c) {
            var oldResult = curResult;
            curResult = func();
            if (! c.firstRun) {
              if (! (equals ? equals(curResult, oldResult) :
                     curResult === oldResult))
                resultDep.changed();
            }
          });
        });
      }

      if (Deps.active) {
        var isNew = resultDep.depend();
        if (isNew) {
          // For each new dependent, schedule a task for after that dependent's
          // invalidation time and the subsequent flush. The task checks
          // whether the computation should be torn down.
          Deps.onInvalidate(function () {
            if (resultDep && ! resultDep.hasDependents()) {
              Deps.afterFlush(function () {
                // use a second afterFlush to bump ourselves to the END of the
                // flush, after computation re-runs have had a chance to
                // re-establish their connections to our computation.
                Deps.afterFlush(function () {
                  if (resultDep && ! resultDep.hasDependents()) {
                    computation.stop();
                    computation = null;
                    resultDep = null;
                  }
                });
              });
            }
          });
        }
      }

      return curResult;
    };

  } else {
    var value = funcOrValue;
    var result = function () {
      return value;
    };
    result._isEmboxedConstant = true;
    return result;
  }
};

////////////////////////////////////////

UI.insert = function (renderedTemplate, parentElement, nextNode) {
  if (! renderedTemplate.dom)
    throw new Error("Expected template rendered with UI.render");

  UI.DomRange.insert(renderedTemplate.dom, parentElement, nextNode);
};

// Insert a DOM node or DomRange into a DOM element or DomRange.
//
// One of three things happens depending on what needs to be inserted into what:
// - `range.add` (anything into DomRange)
// - `UI.DomRange.insert` (DomRange into element)
// - `elem.insertBefore` (node into element)
//
// The optional `before` argument is an existing node or id to insert before in
// the parent element or DomRange.
var insert = function (nodeOrRange, parent, before) {
  if (! parent)
    throw new Error("Materialization parent required");

  if (parent instanceof UI.DomRange) {
    parent.add(nodeOrRange, before);
  } else if (nodeOrRange instanceof UI.DomRange) {
    // parent is an element; inserting a range
    UI.DomRange.insert(nodeOrRange, parent, before);
  } else {
    // parent is an element; inserting an element
    parent.insertBefore(nodeOrRange, before || null); // `null` for IE
  }
};

// Update attributes on `elem` to the dictionary `attrs`, using the
// dictionary of existing `handlers` if provided.
//
// Values in the `attrs` dictionary are in pseudo-DOM form -- a string,
// CharRef, or array of strings and CharRefs -- but they are passed to
// the AttributeHandler in string form.
var updateAttributes = function(elem, newAttrs, handlers) {

  if (handlers) {
    for (var k in handlers) {
      if (! newAttrs.hasOwnProperty(k)) {
        // remove attributes (and handlers) for attribute names
        // that don't exist as keys of `newAttrs` and so won't
        // be visited when traversing it.  (Attributes that
        // exist in the `newAttrs` object but are `null`
        // are handled later.)
        var handler = handlers[k];
        var oldValue = handler.value;
        handler.value = null;
        handler.update(elem, oldValue, null);
        delete handlers[k];
      }
    }
  }

  for (var k in newAttrs) {
    var handler = null;
    var oldValue;
    var value = newAttrs[k];
    if ((! handlers) || (! handlers.hasOwnProperty(k))) {
      if (value !== null) {
        // make new handler
        handler = makeAttributeHandler(elem, k, value);
        if (handlers)
          handlers[k] = handler;
        oldValue = null;
      }
    } else {
      handler = handlers[k];
      oldValue = handler.value;
    }
    if (handler && oldValue !== value) {
      handler.value = value;
      handler.update(elem, oldValue, value);
      if (value === null)
        delete handlers[k];
    }
  }
};

UI.render = function (kind, parentComponent) {
  if (kind.isInited)
    throw new Error("Can't render component instance, only component kind");
  var inst = kind.instantiate(parentComponent);

  var content = (inst.render && inst.render());

  var range = new UI.DomRange;
  inst.dom = range;
  range.component = inst;

  materialize(content, range, null, inst);

  range.removed = function () {
    inst.isDestroyed = true;
    if (inst.destroyed) {
      Deps.nonreactive(function () {
        updateTemplateInstance(inst);
        inst.destroyed.call(inst.templateInstance);
      });
    }
  };

  return inst;
};

UI.renderWithData = function (kind, data, parentComponent) {
  if (! UI.isComponent(kind))
    throw new Error("Component required here");
  if (kind.isInited)
    throw new Error("Can't render component instance, only component kind");
  if (typeof data === 'function')
    throw new Error("Data argument can't be a function");

  return UI.render(kind.extend({data: function () { return data; }}),
                   parentComponent);
};

var contentEquals = function (a, b) {
  if (a instanceof HTML.Raw) {
    return (b instanceof HTML.Raw) && (a.value === b.value);
  } else if (a == null) {
    return (b == null);
  } else {
    return (a === b) &&
      ((typeof a === 'number') || (typeof a === 'boolean') ||
       (typeof a === 'string'));
  }
};

UI.InTemplateScope = function (tmplInstance, content) {
  if (! (this instanceof UI.InTemplateScope))
    // called without `new`
    return new UI.InTemplateScope(tmplInstance, content);

  var parentPtr = tmplInstance.parent;
  if (parentPtr.__isTemplateWith)
    parentPtr = parentPtr.parent;

  this.parentPtr = parentPtr;
  this.content = content;
};

UI.InTemplateScope.prototype.toHTML = function (parentComponent) {
  return HTML.toHTML(this.content, this.parentPtr);
};

UI.InTemplateScope.prototype.toText = function (textMode, parentComponent) {
  return HTML.toText(this.content, textMode, this.parentPtr);
};

// Convert the pseudoDOM `node` into reactive DOM nodes and insert them
// into the element or DomRange `parent`, before the node or id `before`.
var materialize = function (node, parent, before, parentComponent) {
  // XXX should do more error-checking for the case where user is supplying the tags.
  // For example, check that CharRef has `html` and `str` properties and no content.
  // Check that Comment has a single string child and no attributes.  Etc.

  if (node == null) {
    // null or undefined.
    // do nothinge.
  } else if ((typeof node === 'string') || (typeof node === 'boolean') || (typeof node === 'number')) {
    node = String(node);
    insert(document.createTextNode(node), parent, before);
  } else if (node instanceof Array) {
    for (var i = 0; i < node.length; i++)
      materialize(node[i], parent, before, parentComponent);
  } else if (typeof node === 'function') {

    var range = new UI.DomRange;
    var lastContent = null;
    var rangeUpdater = Deps.autorun(function (c) {
      var content = node();
      // normalize content a little, for easier comparison
      if (HTML.isNully(content))
        content = null;
      else if ((content instanceof Array) && content.length === 1)
        content = content[0];

      // update if content is different from last time
      if (! contentEquals(content, lastContent)) {
        lastContent = content;

        if (! c.firstRun)
          range.removeAll();

        Deps.nonreactive(function () {
          materialize(content, range, null, parentComponent);
        });
      }
    });
    range.removed = function () {
      rangeUpdater.stop();
    };
    insert(range, parent, before);
  } else if (node instanceof HTML.Tag) {
    var tagName = node.tagName;
    var elem;
    if (HTML.isKnownSVGElement(tagName) && document.createElementNS) {
      elem = document.createElementNS('http://www.w3.org/2000/svg', tagName);
    } else {
      elem = document.createElement(node.tagName);
    }

    var rawAttrs = node.attrs;
    var children = node.children;
    if (node.tagName === 'textarea') {
      rawAttrs = (rawAttrs || {});
      rawAttrs.value = children;
      children = [];
    };

    if (rawAttrs) {
      var attrUpdater = Deps.autorun(function (c) {
        if (! c.handlers)
          c.handlers = {};

        try {
          var attrs = HTML.evaluateAttributes(rawAttrs, parentComponent);
          var stringAttrs = {};
          if (attrs) {
            for (var k in attrs) {
              stringAttrs[k] = HTML.toText(attrs[k], HTML.TEXTMODE.STRING,
                                           parentComponent);
            }
            updateAttributes(elem, stringAttrs, c.handlers);
          }
        } catch (e) {
          reportUIException(e);
        }
      });
      UI.DomBackend.onRemoveElement(elem, function () {
        attrUpdater.stop();
      });
    }
    materialize(children, elem, null, parentComponent);

    insert(elem, parent, before);
  } else if (typeof node.instantiate === 'function') {
    // component
    var instance = UI.render(node, parentComponent);

    insert(instance.dom, parent, before);
  } else if (node instanceof HTML.CharRef) {
    insert(document.createTextNode(node.str), parent, before);
  } else if (node instanceof HTML.Comment) {
    insert(document.createComment(node.sanitizedValue), parent, before);
  } else if (node instanceof HTML.Raw) {
    // Get an array of DOM nodes by using the browser's HTML parser
    // (like innerHTML).
    var htmlNodes = UI.DomBackend.parseHTML(node.value);
    for (var i = 0; i < htmlNodes.length; i++)
      insert(htmlNodes[i], parent, before);
  } else if (Package['html-tools'] && (node instanceof Package['html-tools'].HTMLTools.Special)) {
    throw new Error("Can't materialize Special tag, it's just an intermediate rep");
  } else if (node instanceof UI.InTemplateScope) {
    materialize(node.content, parent, before, node.parentPtr);
  } else {
    // can't get here
    throw new Error("Unexpected node in htmljs: " + node);
  }
};

// XXX figure out the right names, and namespace, for these.
// for example, maybe some of them go in the HTML package.
UI.materialize = materialize;

UI.body = UI.Component.extend({
  kind: 'body',
  contentParts: [],
  render: function () {
    return this.contentParts;
  },
  // XXX revisit how body works.
  INSTANTIATED: false
});

UI.block = function (renderFunc) {
  return UI.Component.extend({ render: renderFunc });
};

UI.toHTML = function (content, parentComponent) {
  return HTML.toHTML(content, parentComponent);
};

UI.toRawText = function (content, parentComponent) {
  return HTML.toText(content, HTML.TEXTMODE.STRING, parentComponent);
};
<|endoftext|>


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

const Player = require('../public/online-player');

describe('Player movement', function(){
  context('General movement', function(){
    it('moves to the left when A is pressed', function(){
      var keysPressed = {65: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.x, 100);

      var startingX = player.x;
      player.move(keysPressed);

      assert.equal(player.x, (startingX - player.speed));
    });

    it('moves to the right when D is pressed', function(){
      var keysPressed = {68: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.x, 100);

      var startingX = player.x;
      player.move(keysPressed);

      assert.equal(player.x, (startingX + player.speed));
    });

    it('moves up when W is pressed', function(){
      var keysPressed = {87: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.y, 100);

      var startingY = player.y;
      player.move(keysPressed);

      assert.equal(player.y, (startingY - player.speed));
    });

    it('moves down when S is pressed', function(){
      var keysPressed = {83: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.y, 100);

      var startingY = player.y;
      player.move(keysPressed);

      assert.equal(player.y, (startingY + player.speed));
    });
  });

  context('Player circle reaches the edge of the canvas', function(){
    it("cannot move past the left edge", function() {
      var keysPressed = {65: true};
      var player = new Player("id", "Player 1", 7, 100);
      var canMoveLeft = player.canMoveLeft();

      player.move(keysPressed);

      assert.equal(canMoveLeft, false);
      assert.equal(player.x, 7);
    });

    it("cannot move past the right edge", function() {
      var keysPressed = {68: true};
      var player = new Player("id", "Player 1", 1993, 100);
      var canMoveRight = player.canMoveRight();

      player.move(keysPressed);

      assert.equal(canMoveRight, false);
      assert.equal(player.x, 1993);
    });

    it("cannot move past the top edge", function() {
      var keysPressed = {87: true};
      var player = new Player("id", "Player 1", 100, 7);
      var canMoveUp = player.canMoveUp();

      player.move(keysPressed);

      assert.equal(canMoveUp, false);
      assert.equal(player.y, 7);
    });

    it("cannot move past the bottom edge", function() {
      var keysPressed = {83: true};
      var player = new Player("id", "Player 1", 100, 1993);
      var canMoveDown = player.canMoveDown();

      player.move(keysPressed);

      assert.equal(canMoveDown, false);
      assert.equal(player.y, 1993);
    });
  });
});
<commit_msg>Tests reflect updated canvas size<commit_after>const chai = require('chai');
const assert = chai.assert;

const Player = require('../public/online-player');

describe('Player movement', function(){
  context('General movement', function(){
    it('moves to the left when A is pressed', function(){
      var keysPressed = {65: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.x, 100);

      var startingX = player.x;
      player.move(keysPressed);

      assert.equal(player.x, (startingX - player.speed));
    });

    it('moves to the right when D is pressed', function(){
      var keysPressed = {68: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.x, 100);

      var startingX = player.x;
      player.move(keysPressed);

      assert.equal(player.x, (startingX + player.speed));
    });

    it('moves up when W is pressed', function(){
      var keysPressed = {87: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.y, 100);

      var startingY = player.y;
      player.move(keysPressed);

      assert.equal(player.y, (startingY - player.speed));
    });

    it('moves down when S is pressed', function(){
      var keysPressed = {83: true};
      var player = new Player("id", "Player 1", 100, 100);

      assert.equal(player.y, 100);

      var startingY = player.y;
      player.move(keysPressed);

      assert.equal(player.y, (startingY + player.speed));
    });
  });

  context('Player circle reaches the edge of the canvas', function(){
    it("cannot move past the left edge", function() {
      var keysPressed = {65: true};
      var player = new Player("id", "Player 1", 7, 100);
      var canMoveLeft = player.canMoveLeft();

      player.move(keysPressed);

      assert.equal(canMoveLeft, false);
      assert.equal(player.x, 7);
    });

    it("cannot move past the right edge", function() {
      var keysPressed = {68: true};
      var player = new Player("id", "Player 1", 2393, 100);
      var canMoveRight = player.canMoveRight();

      player.move(keysPressed);

      assert.equal(canMoveRight, false);
      assert.equal(player.x, 2393);
    });

    it("cannot move past the top edge", function() {
      var keysPressed = {87: true};
      var player = new Player("id", "Player 1", 100, 7);
      var canMoveUp = player.canMoveUp();

      player.move(keysPressed);

      assert.equal(canMoveUp, false);
      assert.equal(player.y, 7);
    });

    it("cannot move past the bottom edge", function() {
      var keysPressed = {83: true};
      var player = new Player("id", "Player 1", 100, 2393);
      var canMoveDown = player.canMoveDown();

      player.move(keysPressed);

      assert.equal(canMoveDown, false);
      assert.equal(player.y, 2393);
    });
  });
});
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2017    IC Manage Inc.
 *
 *    See the file 'COPYING' for license information.
 */
// const DateTime = require('datetime-converter-nodejs');
// const json = require('JSON');
//
const storPoll = require('../../../vcnc-core/src/lib/pollStorageStats');
//
const conf = require('../vcncSampler.conf');

/**
 * This class is a custom write stream to output VDA messages to multiple files
 * to date names directories
 */
class VcncSampler {
  constructor(dt) {
    this.pmReadBins = {};
    this.vtrqReadBins = {};
    this.samplePeriod = (parseInt(dt, 10) / 1000.0);
    this.startTime = 0;
    this.empty = true;
    this.minIndex = 0;
    this.maxIndex = 0;
    this.vpmMsgCount = 0;
    this.vtrqMsgCount = 0;
    this.ignoredMsgCount = 0;
    this.binCount = 0;
  }

  Init() {
    storPoll.init();
//    console.log('StorageEfficiency: ' + storPoll.currentValue().value);
    return Promise.resolve();
  }

  BinIndex(ts) {
    //  console.log('>>> Start BinIndex');
    const self = this;
     //  console.log('<<< Finished BinIndex');
    return parseInt((ts - self.startTime) / self.samplePeriod, 10);
  }

  Add(jsnMsg) {
//    console.log('>>> Add started');
    const self = this;
    const ts = new Date(jsnMsg.ts).getTime() / 1000;
    let index = self.BinIndex(ts);

    // First call
    //
    // Set start time of data sampling
    //
    if (self.startTime === 0) {
      self.startTime = parseInt(ts, 10);
      const utcTime = Date.now() / 1000;
      self.empty = false;
      self.minIndex = 0;
      self.maxIndex = 0;
      self.binCount = 0;
      index = 0;
    } else {
      index = self.BinIndex(ts);
    }
    // All bins were emptied before and new messages have come
    //
    if (self.empty) {
      if (index > self.minIndex) {
        self.empty = false;
        self.minIndex = index;
      } else {
        self.ignoredMsgCount += 1;
        return;
      }
    }
    // Input messaage is out of time range
    //
    if (index < self.minIndex || index - self.minIndex > conf.MaxBins()) {
      self.ignoredMsgCount += 1;
      return;
    }
    // Set max bin number
    //
    if (index > self.maxIndex) {
      self.maxIndex = index;
//      console.log(`maxIndex = ${self.maxIndex}`);
    }
    // Add number of bytes of read operation to corresponding bin
    //
    const op = jsnMsg.opID;
    const readBytes = parseInt(jsnMsg.errCode, 10);
    //
    switch (op) {
      case 'read':
        self.pmReadBins[index] = (self.pmReadBins[index] === undefined) ?
        readBytes : self.pmReadBins[index] + readBytes;
        self.vpmMsgCount += 1;
        break;
      case 'read_vtrq':
        self.vtrqReadBins[index] = (self.vtrqReadBins[index] === undefined) ?
        readBytes : self.vtrqReadBins[index] + readBytes;
        self.vtrqMsgCount += 1;
        break;
      default:
        console.error('Invalid operation');
        self.ignoredMsgCount += 1;
        return;
    }
    //
    self.msgCount += 1;
//    console.log('<<< Add finished');
  }

  ReleaseBin() {
    const self = this;
//    console.log('>>> Start ReleaseBin ');
//    console.log(`startTime = ${self.startTime} minIndex = ${self.minIndex}`);
    let samplerTs = (self.startTime === 0) ? parseInt(Date.now() / 1000, 10) : self.startTime +
      (self.samplePeriod * self.binCount);
    self.binCount =+ 1;
    // Set time to a center af sample interval
    samplerTs -= Math.round(self.samplePeriod / 2);
    const vpmRead = (self.pmReadBins[self.minIndex] === undefined) ?
      0 : parseInt(self.pmReadBins[self.minIndex] / self.samplePeriod, 10);
    const vtrqRead = (self.vtrqReadBins[self.minIndex] === undefined) ?
      0 : parseInt(self.vtrqReadBins[self.minIndex] / self.samplePeriod, 10);
    const se = storPoll.currentValue();
    // Set output bin
    //
	const vpmLocalRead = Math.max(vpmRead - vtrqRead, 0);
    const bin = {
      storageEfficiency: se.value,
      sum_st_size: parseInt(se.st_size, 10),
      sum_extents: parseInt(se.extents, 10),
      rVtrq: Math.log10(vtrqRead + 1),
      //  Any reads resolved on the vtrq were not resolved on the vpm. <B8569>
      rVpm: Math.log10(vpmLocalRead + 1),
      sampleTimestamp: samplerTs,
    };
//    console.log(`Bin: ${json.stringify(bin)}`);
    // Delete processed bin
    //
//    let keys1 = Object.keys(self.pmReadBins);
//    for (let i = 0; i < keys1.length; i++) {
//      console.log(`Key[${i}] = ${keys1[i]}`);
//    }

    if (self.pmReadBins[self.minIndex] !== undefined) delete self.pmReadBins[self.minIndex];
    if (self.vtrqReadBins[self.minIndex] !== undefined) delete self.vtrqReadBins[self.minIndex];
//    console.log(`ReleaseBin 1: minIndex= ${self.minIndex} maxIndex = ${self.maxIndex}`);
    if (parseInt(self.minIndex, 10) === parseInt(self.maxIndex, 10)) {
      self.empty = true;
      self.maxIndex = 0;
      self.minIndex = 0;
    } else if (self.maxIndex > 0) {
//      const keys = Object.keys(self.pmReadBins);
//      self.minIndex = keys[0];
      self.minIndex += 1;
    }
//    console.log(`Bin: ${json.stringify(bin)}`);
//    console.log(`minIndex = ${self.minIndex} maxIndex = ${self.maxIndex}`);
//    console.log('<<< Finished ReleaseBin');
    return bin;
  }

  MessageCount() { return (this.vpmMsgCount + this.vtrqMsgCount); }
  VpmMessageCount() { return this.vpmMsgCount; }
  VtrqMessageCount() { return this.vtrqMsgCount; }
  IgnoreMessageCount() { return this.ignoredMsgCount; }
}

function CreateVcncSampler(dt) {
  return new VcncSampler(dt);
}

module.exports = {
  CreateVcncSampler,
};
<commit_msg>Reduce number of ignored messages<commit_after>/*
 *    Copyright (C) 2017    IC Manage Inc.
 *
 *    See the file 'COPYING' for license information.
 */
// const DateTime = require('datetime-converter-nodejs');
// const json = require('JSON');
//
const storPoll = require('../../../vcnc-core/src/lib/pollStorageStats');
//
const conf = require('../vcncSampler.conf');

/**
 * This class is a custom write stream to output VDA messages to multiple files
 * to date names directories
 */
class VcncSampler {
  constructor(dt) {
    this.pmReadBins = {};
    this.vtrqReadBins = {};
    this.samplePeriod = (parseInt(dt, 10) / 1000.0);
    this.startTime = 0;
    this.empty = true;
    this.minIndex = 0;
    this.maxIndex = 0;
    this.vpmMsgCount = 0;
    this.vtrqMsgCount = 0;
    this.ignoredMsgCount = 0;
    this.binCount = 0;
  }

  Init() {
    storPoll.init();
//    console.log('StorageEfficiency: ' + storPoll.currentValue().value);
    return Promise.resolve();
  }

  BinIndex(ts) {
    //  console.log('>>> Start BinIndex');
    const self = this;
     //  console.log('<<< Finished BinIndex');
    return parseInt((ts - self.startTime) / self.samplePeriod, 10);
  }

  Add(jsnMsg) {
//    console.log('>>> Add started');
    const self = this;
    const ts = new Date(jsnMsg.ts).getTime() / 1000;
    let index = self.BinIndex(ts);

    // First call
    //
    // Set start time of data sampling
    //
    if (self.startTime === 0) {
      self.startTime = parseInt(ts, 10);
      const utcTime = Date.now() / 1000;
      self.empty = false;
      self.minIndex = 0;
      self.maxIndex = 0;
      self.binCount = 0;
      index = 0;
    } else {
      index = self.BinIndex(ts);
    }
    // All bins were emptied before and new messages have come
    //
    if (self.empty) {
      if (index >= self.minIndex) {
        self.empty = false;
        self.minIndex = index;
      } else {
        self.ignoredMsgCount += 1;
        return;
      }
    }
    // Input messaage is out of time range
    //
    if (index < self.minIndex || index - self.minIndex > conf.MaxBins()) {
      self.ignoredMsgCount += 1;
      return;
    }
    // Set max bin number
    //
    if (index > self.maxIndex) {
      self.maxIndex = index;
//      console.log(`maxIndex = ${self.maxIndex}`);
    }
    // Add number of bytes of read operation to corresponding bin
    //
    const op = jsnMsg.opID;
    const readBytes = parseInt(jsnMsg.errCode, 10);
    //
    switch (op) {
      case 'read':
        self.pmReadBins[index] = (self.pmReadBins[index] === undefined) ?
        readBytes : self.pmReadBins[index] + readBytes;
        self.vpmMsgCount += 1;
        break;
      case 'read_vtrq':
        self.vtrqReadBins[index] = (self.vtrqReadBins[index] === undefined) ?
        readBytes : self.vtrqReadBins[index] + readBytes;
        self.vtrqMsgCount += 1;
        break;
      default:
        console.error('Invalid operation');
        self.ignoredMsgCount += 1;
        return;
    }
    //
    self.msgCount += 1;
//    console.log('<<< Add finished');
  }

  ReleaseBin() {
    const self = this;
//    console.log('>>> Start ReleaseBin ');
//    console.log(`startTime = ${self.startTime} minIndex = ${self.minIndex}`);
    let samplerTs = (self.startTime === 0) ? parseInt(Date.now() / 1000, 10) : self.startTime +
      (self.samplePeriod * self.binCount);
    self.binCount =+ 1;
    // Set time to a center af sample interval
    samplerTs -= Math.round(self.samplePeriod / 2);
    const vpmRead = (self.pmReadBins[self.minIndex] === undefined) ?
      0 : parseInt(self.pmReadBins[self.minIndex] / self.samplePeriod, 10);
    const vtrqRead = (self.vtrqReadBins[self.minIndex] === undefined) ?
      0 : parseInt(self.vtrqReadBins[self.minIndex] / self.samplePeriod, 10);
    const se = storPoll.currentValue();
    // Set output bin
    //
	const vpmLocalRead = Math.max(vpmRead - vtrqRead, 0);
    const bin = {
      storageEfficiency: se.value,
      sum_st_size: parseInt(se.st_size, 10),
      sum_extents: parseInt(se.extents, 10),
      rVtrq: Math.log10(vtrqRead + 1),
      //  Any reads resolved on the vtrq were not resolved on the vpm. <B8569>
      rVpm: Math.log10(vpmLocalRead + 1),
      sampleTimestamp: samplerTs,
    };
//    console.log(`Bin: ${json.stringify(bin)}`);
    // Delete processed bin
    //
//    let keys1 = Object.keys(self.pmReadBins);
//    for (let i = 0; i < keys1.length; i++) {
//      console.log(`Key[${i}] = ${keys1[i]}`);
//    }

    if (self.pmReadBins[self.minIndex] !== undefined) delete self.pmReadBins[self.minIndex];
    if (self.vtrqReadBins[self.minIndex] !== undefined) delete self.vtrqReadBins[self.minIndex];
//    console.log(`ReleaseBin 1: minIndex= ${self.minIndex} maxIndex = ${self.maxIndex}`);
    if (parseInt(self.minIndex, 10) === parseInt(self.maxIndex, 10)) {
      self.empty = true;
      self.maxIndex = 0;
      self.minIndex = 0;
    } else if (self.maxIndex > 0) {
//      const keys = Object.keys(self.pmReadBins);
//      self.minIndex = keys[0];
      self.minIndex += 1;
    }
//    console.log(`Bin: ${json.stringify(bin)}`);
//    console.log(`minIndex = ${self.minIndex} maxIndex = ${self.maxIndex}`);
//    console.log('<<< Finished ReleaseBin');
    return bin;
  }

  MessageCount() { return (this.vpmMsgCount + this.vtrqMsgCount); }
  VpmMessageCount() { return this.vpmMsgCount; }
  VtrqMessageCount() { return this.vtrqMsgCount; }
  IgnoreMessageCount() { return this.ignoredMsgCount; }
}

function CreateVcncSampler(dt) {
  return new VcncSampler(dt);
}

module.exports = {
  CreateVcncSampler,
};
<|endoftext|>


<commit_before>var melted_node = require('melted-node')
,   melted_xml  = require('node-mlt')
,   Status      = require('../../api/Status')
,   StatusClip  = require('../../api/StatusClip')
,   fs          = require('fs')
,   config      = require('mbc-common').config.Mosto.Melted
,   Q           = require('q')
,   utils       = require('../../utils')
,   logger      = require('../../logger').addLogger('MELTED-NODE-DRIVER')
,   melted_log  = require('../../logger').addLogger('MELTED-NODE')
,   uuid        = require('node-uuid')
;

function melted(host, port, timeout) {
    var self = this;
    this.uuid = uuid.v4();
    logger.debug(self.uuid + " - Creating server instance [" + host + ":" + port + "]");
    this.mlt = new melted_node(host, port, melted_log, timeout);
    this.commandQueue = Q.resolve();
    logger.debug(self.uuid + " - Server instance created [" + this.mlt.host + ":" + this.mlt.port + "]");
}

melted.prototype._sendCommand = function(command) {
    var self = this;
    logger.debug(self.uuid + " - Sending command: " + command);
    return self.mlt.sendCommand(command, "200 OK");
};

melted.prototype.sendCommand = function(command) {
    var self = this;
    var ret = Q.defer();
    this.commandQueue = this.commandQueue.then(function() {
        ret.resolve(self._sendCommand(command));
    });
    return ret.promise;
};

melted.prototype.getServerPlaylist = function() {
    var self = this;
    logger.debug(self.uuid + " - Sending command: LIST U0");
    return self.mlt.sendCommand("list u0", "201 OK").then(function(response) {
        // HACK: Converting the promise object to a string :)
        var data = "." + response;

        var split = data.split("\r\n");
        var clips = [];
        for (var i = 0; i < split.length; i++) {
            var line = split[i];
            var parse = line.split(" ");

            if (parse.length >=7 ) {
                logger.debug(self.uuid + " - getServerPlaylist:" + line );
                var index       = parse[0];
                // remove sorrounding " from filename
                var file        = parse[1].replace(/^"|"$/g,'');
                var inFrame     = parse[2];
                var outFrame    = parse[3];
                var real_length = parse[4];
                var calc_length = parse[5];
                var fps         = parse[6];

                var aux         = file.split("/");
                var filename    = aux[aux.length - 1];
                var playlistId  = utils.getPlaylistIdFromXmlFileName(filename);
                var clipId      = utils.getClipIdFromXmlFileName(filename);

                var clip = new StatusClip(clipId, index, playlistId, fps, 0, real_length);

                clips.push(clip);
            }
        }
        return clips;
    }).fail(function(error) {
        var err = new Error(self.uuid + " - Error getting server playlist: " + error);
        logger.error(err.message);
        throw err;
    });
};

melted.prototype.getServerStatus = function() {
    var self = this;
    logger.debug(self.uuid + " - Sending command: USTA U0");
    return self.mlt.sendCommand("usta u0", "202 OK").then(function(response) {
        // HACK: Converting the promise object to a string :)
        var data = "." + response;

        var split = data.split("\r\n");

        if (split.length>=2) {
            var parse = split[1].split(" ");
            if (parse.length>=17) {
                var status       = parse[1];
                // remove sorrounding " from filename
                var file         = parse[2].replace(/^"|"$/g,'');
                var currentFrame = parse[3];
                var fps          = parse[5];
                var inPoint      = parse[6];
                var outPoint     = parse[7];
                var length       = parse[8];
                var index        = parse[16];

                var st = undefined;

                if (file.split(".").length > 1) {
                    var aux         = file.split("/");
                    var filename    = aux[aux.length - 1];
                    var playlistId  = utils.getPlaylistIdFromXmlFileName(filename);
                    var clipId      = utils.getClipIdFromXmlFileName(filename);

                    var clip = new StatusClip(clipId, index, playlistId, fps, currentFrame, length);
                    var pos = utils.getCurrentPosFromClip(currentFrame, length);
                    st = new Status(status, clip, pos);
                } else {
                    st = new Status(status, undefined, 0);
                }
                return st;
            }
        }
        var err = new Error(self.uuid + " - Error getting server status in response object: " + response)
        throw (err);
    }).fail(function() {
        var err = new Error(self.uuid + " - Error getting server status: " + error);
        logger.error(err.message);
        throw err;
    });
};

melted.prototype.isConnected = function() {
    var self = this;
    return self.mlt.connected;
};

melted.prototype.initServer = function() {
    var self = this;
    logger.info(self.uuid + " - Connecting to server instance [" + self.mlt.host + ":" + self.mlt.port + "]");

    var deferred = Q.defer();

    var result = self.mlt.connect();

    result.then(function(response) {
        logger.debug(self.uuid + " - Sending command: ULS");
        var aux = self.mlt.sendCommand("ULS", "201 OK");
        aux.then(function(response) {
            if (response.indexOf("U0") === -1) {
                var err = new Error(self.uuid + " - Unit 0 not found");
                logger.error(err.message);
                deferred.reject(err);
            } else {
                deferred.resolve("OK");
            }
        }, function(error) {
            var err = new Error(self.uuid + " - Could not query Unit status: " + error);
            logger.error(err.message, error);
            deferred.reject(err);
        });
    }, function(error) {
        var err = new Error(self.uuid + " - Could not connect to Melted: " + error);
        logger.error(err.message, error);
        deferred.reject(err);
    });

    return deferred.promise;
};

melted.prototype.stopServer = function() {
    var self = this;
    logger.info(self.uuid + " - Disconnecting from server instance [" + self.mlt.host + ":" + self.mlt.port + "]");

    return self.mlt.disconnect();
};

melted.prototype.sendClip = function(clip, command) {
    var self = this;
    var xml = new melted_xml();

    var type     = clip.type;
    var file     = clip.file;
    var filename = utils.getXmlFileNameFromClip(clip);

    //            var filters = self.config.types[type].filters;

    logger.debug(self.uuid + " - Generating file " + filename);

    logger.debug(self.uuid + " - Adding media [" + file + "] to file " + filename);
    var video = new melted_xml.Producer.Video({ source: file, startFrame: clip.in, length: clip.length });
    xml.push(video);

    logger.debug(self.uuid + " - Creating playlist xml object for file " + filename);
    var pl = new melted_xml.Playlist;
    pl.entry({producer: video});
    xml.push(pl);

    logger.debug(self.uuid + " - Creating track xml object for file " + filename);
    var track = new melted_xml.Multitrack.Track(pl);

    logger.debug(self.uuid + " - Creating multitrack xml object for file " + filename);
    var multitrack = new melted_xml.Multitrack;
    multitrack.addTrack(track);

    logger.debug(self.uuid + " - Creating tractor xml object for file " + filename);
    var tractor = new melted_xml.Tractor;
    tractor.push(multitrack);

    //            console.log("mbc-mosto: [INFO] Creating filter xml objects for file " + filename);
    //            console.log("mbc-mosto: [INFO] Filters: " + filters);
    //            filters.forEach(function(element, index, array){
    //                var filter = self.config.filters[element];
    //                var filterObj = new melted_xml.Filter[filter.filter.charAt(0).toUpperCase() + filter.filter.slice(1)](filter.properties);
    //                console.log("mbc-mosto: [INFO] Adding filter " + filter.filter);
    //                console.log(filter.properties);
    //                tractor.push(filterObj);
    //            });

    logger.debug(self.uuid + " - Pushing xml for file " + filename);
    xml.push(tractor);

    //        var fileName = file.substring(file.lastIndexOf("/") + 1);
    var xmlFile = config.playlists_xml_dir + "/" + filename;

    var deferred = Q.defer();

    logger.debug(self.uuid + " - Writing file " + xmlFile);
    fs.writeFile(xmlFile, xml.toString({pretty:true}), function(err){
        if (err) {
            deferred.reject(err);
        } else {
            logger.debug(self.uuid + " - File ready: " + xmlFile);
            deferred.resolve(self.sendCommand(command.replace("{xmlFile}", xmlFile)));
        }
    });
    return deferred.promise;
};

melted.prototype.loadClip = function(clip) {
    var self = this;
    //Load clip removing the whole playlist and starting playback
    return self.sendClip(clip, "LOAD U0 {xmlFile}");
};
melted.prototype.appendClip = function(clip) {
    var self = this;
    //Appends clip to the end of the playlist
    return self.sendClip(clip, "APND UO {xmlFile}");
};
melted.prototype.insertClip = function(clip, index) {
    var self = this;
    //Insert clip at specified index
    return self.sendClip(clip, "INSERT UO {xmlFile} " + index);
};
melted.prototype.removeClip = function(index) {
    var self = this;
    //Removes clip at specified index
    return self.sendCommand("REMOVE U0 " + index);
};
melted.prototype.cleanPlaylist = function() {
    var self = this;
    //Removes all clips but playing clip
    return self.sendCommand("CLEAN U0");
};
//    melted.prototype.wipePlaylist = function(successCallback, errorCallback) {
//        var self = this;
//        //Removes all clips before playing clip
//        self.sendCommand("WIPE U0", successCallback, errorCallback);
//    };
melted.prototype.clearPlaylist = function() {
    var self = this;
    //Removes all clips, including playing clip
    return self.sendCommand("CLEAR U0");
};
melted.prototype.moveClip = function(oldIndex, newIndex) {
    var self = this;
    //Moves the clip at oldIndex to newIndex (use it with getServerPlaylist)
    return self.sendCommand("MOVE U0 " + oldIndex + " " + newIndex);
};
melted.prototype.play = function() {
    var self = this;
    //Play
    return self.sendCommand("PLAY U0");
};
melted.prototype.stop = function() {
    var self = this;
    //Stop
    return self.sendCommand("STOP U0");
};
melted.prototype.pause = function() {
    var self = this;
    //Pause
    return self.sendCommand("PAUSE U0");
};
melted.prototype.goto = function(index, frame) {
    var self = this;
    //Starts playing clip at specified index and frame (use with getServerPlaylist and getServerStatus)
    return self.sendCommand("GOTO U0 " + frame + " " + index);
};

exports = module.exports = function(host, port, timeout) {
    var melted_node_driver = new melted(host, port, timeout);
    return melted_node_driver;
};

<commit_msg>don't use mlt.sendCommand, use this.sendCommand<commit_after>var melted_node = require('melted-node')
,   melted_xml  = require('node-mlt')
,   Status      = require('../../api/Status')
,   StatusClip  = require('../../api/StatusClip')
,   fs          = require('fs')
,   config      = require('mbc-common').config.Mosto.Melted
,   Q           = require('q')
,   utils       = require('../../utils')
,   logger      = require('../../logger').addLogger('MELTED-NODE-DRIVER')
,   melted_log  = require('../../logger').addLogger('MELTED-NODE')
,   uuid        = require('node-uuid')
;

function melted(host, port, timeout) {
    var self = this;
    this.uuid = uuid.v4();
    logger.debug(self.uuid + " - Creating server instance [" + host + ":" + port + "]");
    this.mlt = new melted_node(host, port, melted_log, timeout);
    this.commandQueue = Q.resolve();
    logger.debug(self.uuid + " - Server instance created [" + this.mlt.host + ":" + this.mlt.port + "]");
}

melted.prototype._sendCommand = function(command) {
    var self = this;
    logger.debug(self.uuid + " - Sending command: " + command);
    return self.mlt.sendCommand(command, "200 OK");
};

melted.prototype.sendCommand = function(command) {
    var self = this;
    var ret = Q.defer();
    this.commandQueue = this.commandQueue.then(function() {
        ret.resolve(self._sendCommand(command));
    });
    return ret.promise;
};

melted.prototype.getServerPlaylist = function() {
    var self = this;
    return self.sendCommand("list u0", "201 OK").then(function(response) {
        // HACK: Converting the promise object to a string :)
        var data = "." + response;

        var split = data.split("\r\n");
        var clips = [];
        for (var i = 0; i < split.length; i++) {
            var line = split[i];
            var parse = line.split(" ");

            if (parse.length >=7 ) {
                logger.debug(self.uuid + " - getServerPlaylist:" + line );
                var index       = parse[0];
                // remove sorrounding " from filename
                var file        = parse[1].replace(/^"|"$/g,'');
                var inFrame     = parse[2];
                var outFrame    = parse[3];
                var real_length = parse[4];
                var calc_length = parse[5];
                var fps         = parse[6];

                var aux         = file.split("/");
                var filename    = aux[aux.length - 1];
                var playlistId  = utils.getPlaylistIdFromXmlFileName(filename);
                var clipId      = utils.getClipIdFromXmlFileName(filename);

                var clip = new StatusClip(clipId, index, playlistId, fps, 0, real_length);

                clips.push(clip);
            }
        }
        return clips;
    }).fail(function(error) {
        var err = new Error(self.uuid + " - Error getting server playlist: " + error);
        logger.error(err.message);
        throw err;
    });
};

melted.prototype.getServerStatus = function() {
    var self = this;
    return self.sendCommand("usta u0", "202 OK").then(function(response) {
        // HACK: Converting the promise object to a string :)
        var data = "." + response;

        var split = data.split("\r\n");

        if (split.length>=2) {
            var parse = split[1].split(" ");
            if (parse.length>=17) {
                var status       = parse[1];
                // remove sorrounding " from filename
                var file         = parse[2].replace(/^"|"$/g,'');
                var currentFrame = parse[3];
                var fps          = parse[5];
                var inPoint      = parse[6];
                var outPoint     = parse[7];
                var length       = parse[8];
                var index        = parse[16];

                var st = undefined;

                if (file.split(".").length > 1) {
                    var aux         = file.split("/");
                    var filename    = aux[aux.length - 1];
                    var playlistId  = utils.getPlaylistIdFromXmlFileName(filename);
                    var clipId      = utils.getClipIdFromXmlFileName(filename);

                    var clip = new StatusClip(clipId, index, playlistId, fps, currentFrame, length);
                    var pos = utils.getCurrentPosFromClip(currentFrame, length);
                    st = new Status(status, clip, pos);
                } else {
                    st = new Status(status, undefined, 0);
                }
                return st;
            }
        }
        var err = new Error(self.uuid + " - Error getting server status in response object: " + response)
        throw (err);
    }).fail(function() {
        var err = new Error(self.uuid + " - Error getting server status: " + error);
        logger.error(err.message);
        throw err;
    });
};

melted.prototype.isConnected = function() {
    var self = this;
    return self.mlt.connected;
};

melted.prototype.initServer = function() {
    var self = this;
    logger.info(self.uuid + " - Connecting to server instance [" + self.mlt.host + ":" + self.mlt.port + "]");

    var deferred = Q.defer();

    var result = self.mlt.connect();

    result.then(function(response) {
        var aux = self.sendCommand("ULS", "201 OK");
        aux.then(function(response) {
            if (response.indexOf("U0") === -1) {
                var err = new Error(self.uuid + " - Unit 0 not found");
                logger.error(err.message);
                deferred.reject(err);
            } else {
                deferred.resolve("OK");
            }
        }, function(error) {
            var err = new Error(self.uuid + " - Could not query Unit status: " + error);
            logger.error(err.message, error);
            deferred.reject(err);
        });
    }, function(error) {
        var err = new Error(self.uuid + " - Could not connect to Melted: " + error);
        logger.error(err.message, error);
        deferred.reject(err);
    });

    return deferred.promise;
};

melted.prototype.stopServer = function() {
    var self = this;
    logger.info(self.uuid + " - Disconnecting from server instance [" + self.mlt.host + ":" + self.mlt.port + "]");

    return self.mlt.disconnect();
};

melted.prototype.sendClip = function(clip, command) {
    var self = this;
    var xml = new melted_xml();

    var type     = clip.type;
    var file     = clip.file;
    var filename = utils.getXmlFileNameFromClip(clip);

    //            var filters = self.config.types[type].filters;

    logger.debug(self.uuid + " - Generating file " + filename);

    logger.debug(self.uuid + " - Adding media [" + file + "] to file " + filename);
    var video = new melted_xml.Producer.Video({ source: file, startFrame: clip.in, length: clip.length });
    xml.push(video);

    logger.debug(self.uuid + " - Creating playlist xml object for file " + filename);
    var pl = new melted_xml.Playlist;
    pl.entry({producer: video});
    xml.push(pl);

    logger.debug(self.uuid + " - Creating track xml object for file " + filename);
    var track = new melted_xml.Multitrack.Track(pl);

    logger.debug(self.uuid + " - Creating multitrack xml object for file " + filename);
    var multitrack = new melted_xml.Multitrack;
    multitrack.addTrack(track);

    logger.debug(self.uuid + " - Creating tractor xml object for file " + filename);
    var tractor = new melted_xml.Tractor;
    tractor.push(multitrack);

    //            console.log("mbc-mosto: [INFO] Creating filter xml objects for file " + filename);
    //            console.log("mbc-mosto: [INFO] Filters: " + filters);
    //            filters.forEach(function(element, index, array){
    //                var filter = self.config.filters[element];
    //                var filterObj = new melted_xml.Filter[filter.filter.charAt(0).toUpperCase() + filter.filter.slice(1)](filter.properties);
    //                console.log("mbc-mosto: [INFO] Adding filter " + filter.filter);
    //                console.log(filter.properties);
    //                tractor.push(filterObj);
    //            });

    logger.debug(self.uuid + " - Pushing xml for file " + filename);
    xml.push(tractor);

    //        var fileName = file.substring(file.lastIndexOf("/") + 1);
    var xmlFile = config.playlists_xml_dir + "/" + filename;

    var deferred = Q.defer();

    logger.debug(self.uuid + " - Writing file " + xmlFile);
    fs.writeFile(xmlFile, xml.toString({pretty:true}), function(err){
        if (err) {
            deferred.reject(err);
        } else {
            logger.debug(self.uuid + " - File ready: " + xmlFile);
            deferred.resolve(self.sendCommand(command.replace("{xmlFile}", xmlFile)));
        }
    });
    return deferred.promise;
};

melted.prototype.loadClip = function(clip) {
    var self = this;
    //Load clip removing the whole playlist and starting playback
    return self.sendClip(clip, "LOAD U0 {xmlFile}");
};
melted.prototype.appendClip = function(clip) {
    var self = this;
    //Appends clip to the end of the playlist
    return self.sendClip(clip, "APND UO {xmlFile}");
};
melted.prototype.insertClip = function(clip, index) {
    var self = this;
    //Insert clip at specified index
    return self.sendClip(clip, "INSERT UO {xmlFile} " + index);
};
melted.prototype.removeClip = function(index) {
    var self = this;
    //Removes clip at specified index
    return self.sendCommand("REMOVE U0 " + index);
};
melted.prototype.cleanPlaylist = function() {
    var self = this;
    //Removes all clips but playing clip
    return self.sendCommand("CLEAN U0");
};
//    melted.prototype.wipePlaylist = function(successCallback, errorCallback) {
//        var self = this;
//        //Removes all clips before playing clip
//        self.sendCommand("WIPE U0", successCallback, errorCallback);
//    };
melted.prototype.clearPlaylist = function() {
    var self = this;
    //Removes all clips, including playing clip
    return self.sendCommand("CLEAR U0");
};
melted.prototype.moveClip = function(oldIndex, newIndex) {
    var self = this;
    //Moves the clip at oldIndex to newIndex (use it with getServerPlaylist)
    return self.sendCommand("MOVE U0 " + oldIndex + " " + newIndex);
};
melted.prototype.play = function() {
    var self = this;
    //Play
    return self.sendCommand("PLAY U0");
};
melted.prototype.stop = function() {
    var self = this;
    //Stop
    return self.sendCommand("STOP U0");
};
melted.prototype.pause = function() {
    var self = this;
    //Pause
    return self.sendCommand("PAUSE U0");
};
melted.prototype.goto = function(index, frame) {
    var self = this;
    //Starts playing clip at specified index and frame (use with getServerPlaylist and getServerStatus)
    return self.sendCommand("GOTO U0 " + frame + " " + index);
};

exports = module.exports = function(host, port, timeout) {
    var melted_node_driver = new melted(host, port, timeout);
    return melted_node_driver;
};

<|endoftext|>