(function() {
    var BALLOT_X = '✗';
    var CHECKMARK = '✓';
    var DOWN_RIGHT_ARROW = '↳';
    var PASSED = 'passed';
    var lastSpecStartTime;
    function ConsoleReporter(options) {
        var startTime = new Date().getTime();
        var errorCount = 0, pending = [];
        this.jasmineStarted = function (obj) {
            print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.');
        };
        this.jasmineDone = function (obj) {
            var ERROR = errorCount === 1 ? 'error' : 'errors';
            var endTime = new Date().getTime();
            print('<hr />');
            if (errorCount === 0) {
                print ('<span style="color:green">All enabled tests passed!</span>');
            } else {
                print('<span style="color:red">Tests completed with ' +
                    errorCount + ' ' + ERROR + '.<span>');
            }
            if (pending.length) {
                print ('<span style="color:darkorange">disabled: <br />&nbsp;&nbsp;&nbsp;'+
                       pending.join('<br />&nbsp;&nbsp;&nbsp;')+'</span>');
            }
            print('Tests completed in ' + (endTime - startTime) + 'ms.');
        };
        this.suiteStarted = function(obj) {
            print(obj.fullName);
        };
        this.suiteDone = function(obj) {
            print('');
        };
        this.specStarted = function(obj) {
            lastSpecStartTime = new Date().getTime();
        };
        this.specDone = function(obj) {
            if (obj.status === 'pending') {
                pending.push(obj.fullName);
                return print('...(pending ' + obj.fullName +')');
            }
            var specEndTime = new Date().getTime();
            var symbol = obj.status === PASSED ?
                '<span style="color:green">' + CHECKMARK + '</span>' :
                '<span style="color:red">' + BALLOT_X + '</span>';
            print('... ' + obj.fullName + ' ' + symbol + ' ' + '<span style="color:orange">[' +
                (specEndTime - lastSpecStartTime) + 'ms]</span>');

            var specErrors = obj.failedExpectations.length;
            errorCount += specErrors;
            for (var i = 0; i < specErrors; i++) {
                print('<span style="margin-right:0.5em"></span>' + DOWN_RIGHT_ARROW +
                    '<span style="color:red">  ' +
                    obj.failedExpectations[i].message + '</span>');
            }
        };
        return this;
    }

    setTimeout = Script.setTimeout;
    setInterval = Script.setInterval;
    clearTimeout = Script.clearTimeout;
    clearInterval = Script.clearInterval;

    var jasmine = this.jasmine = jasmineRequire.core(jasmineRequire);

    var env = jasmine.getEnv();

    env.addReporter(new ConsoleReporter());

    var jasmineInterface = jasmineRequire.interface(jasmine, env);

    extend(this, jasmineInterface);

    function extend(destination, source) {
        for (var property in source) {
            if (source.hasOwnProperty(property)) {
                destination[property] = source[property];
            }
        }
        return destination;
    }
}());