<commit_before>/* global describe, it, expect */
import webdriver from 'next-webdriver'

export default function (context) {
  describe('Render via browser', () => {
    it('should render the home page', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#home-page p').text()

      expect(text).toBe('This is the home page')
      browser.close()
    })

    it('should do navigations via Link', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#about-via-link').click()
          .waitForElementByCss('#about-page')
          .elementByCss('#about-page p').text()

      expect(text).toBe('This is the About page')
      browser.close()
    })

    it('should do navigations via Router', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#about-via-router').click()
          .waitForElementByCss('#about-page')
          .elementByCss('#about-page p').text()

      expect(text).toBe('This is the About page')
      browser.close()
    })

    it('should do run client side javascript', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#counter').click()
          .waitForElementByCss('#counter-page')
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-page p').text()

      expect(text).toBe('Counter: 2')
      browser.close()
    })

    it('should render pages using getInitialProps', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#get-initial-props').click()
          .waitForElementByCss('#dynamic-page')
          .elementByCss('#dynamic-page p').text()

      expect(text).toBe('cool dynamic text')
      browser.close()
    })

    it('should render dynamic pages with custom urls', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#dynamic-1').click()
          .waitForElementByCss('#dynamic-page')
          .elementByCss('#dynamic-page p').text()

      expect(text).toBe('next export is nice')
      browser.close()
    })

    it('should support client side naviagtion', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#counter').click()
          .waitForElementByCss('#counter-page')
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-page p').text()

      expect(text).toBe('Counter: 2')

      // let's go back and come again to this page:
      const textNow = await browser
        .elementByCss('#go-back').click()
        .waitForElementByCss('#home-page')
        .elementByCss('#counter').click()
        .waitForElementByCss('#counter-page')
        .elementByCss('#counter-page p').text()

      expect(textNow).toBe('Counter: 2')

      browser.close()
    })

    it('should render dynamic import components in the client', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#dynamic-imports-page').click()
          .waitForElementByCss('#dynamic-imports-page')
          .elementByCss('#dynamic-imports-page p').text()

      expect(text).toBe('Welcome to dynamic imports.')
      browser.close()
    })

    describe('pages in the nested level: level1', () => {
      it('should render the home page', async () => {
        const browser = await webdriver(context.port, '/')
        const text = await browser
            .elementByCss('#level1-home-page').click()
            .waitForElementByCss('#level1-home-page')
            .elementByCss('#level1-home-page p').text()

        expect(text).toBe('This is the Level1 home page')
        browser.close()
      })

      it('should render the about page', async () => {
        const browser = await webdriver(context.port, '/')
        const text = await browser
            .elementByCss('#level1-about-page').click()
            .waitForElementByCss('#level1-about-page')
            .elementByCss('#level1-about-page p').text()

        expect(text).toBe('This is the Level1 about page')
        browser.close()
      })
    })
  })
}
<commit_msg>Possible fix for dynamic import test in travis.<commit_after>/* global describe, it, expect */
import webdriver from 'next-webdriver'
import { waitFor } from 'next-test-utils'

export default function (context) {
  describe('Render via browser', () => {
    it('should render the home page', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#home-page p').text()

      expect(text).toBe('This is the home page')
      browser.close()
    })

    it('should do navigations via Link', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#about-via-link').click()
          .waitForElementByCss('#about-page')
          .elementByCss('#about-page p').text()

      expect(text).toBe('This is the About page')
      browser.close()
    })

    it('should do navigations via Router', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#about-via-router').click()
          .waitForElementByCss('#about-page')
          .elementByCss('#about-page p').text()

      expect(text).toBe('This is the About page')
      browser.close()
    })

    it('should do run client side javascript', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#counter').click()
          .waitForElementByCss('#counter-page')
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-page p').text()

      expect(text).toBe('Counter: 2')
      browser.close()
    })

    it('should render pages using getInitialProps', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#get-initial-props').click()
          .waitForElementByCss('#dynamic-page')
          .elementByCss('#dynamic-page p').text()

      expect(text).toBe('cool dynamic text')
      browser.close()
    })

    it('should render dynamic pages with custom urls', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#dynamic-1').click()
          .waitForElementByCss('#dynamic-page')
          .elementByCss('#dynamic-page p').text()

      expect(text).toBe('next export is nice')
      browser.close()
    })

    it('should support client side naviagtion', async () => {
      const browser = await webdriver(context.port, '/')
      const text = await browser
          .elementByCss('#counter').click()
          .waitForElementByCss('#counter-page')
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-increase').click()
          .elementByCss('#counter-page p').text()

      expect(text).toBe('Counter: 2')

      // let's go back and come again to this page:
      const textNow = await browser
        .elementByCss('#go-back').click()
        .waitForElementByCss('#home-page')
        .elementByCss('#counter').click()
        .waitForElementByCss('#counter-page')
        .elementByCss('#counter-page p').text()

      expect(textNow).toBe('Counter: 2')

      browser.close()
    })

    it('should render dynamic import components in the client', async () => {
      const browser = await webdriver(context.port, '/')
      await browser
          .elementByCss('#dynamic-imports-page').click()
          .waitForElementByCss('#dynamic-imports-page')

      // Wait until browser loads the dynamic import chunk
      // TODO: We may need to find a better way to do this
      await waitFor(5000)

      const text = await browser
          .elementByCss('#dynamic-imports-page p').text()

      expect(text).toBe('Welcome to dynamic imports.')
      browser.close()
    })

    describe('pages in the nested level: level1', () => {
      it('should render the home page', async () => {
        const browser = await webdriver(context.port, '/')
        const text = await browser
            .elementByCss('#level1-home-page').click()
            .waitForElementByCss('#level1-home-page')
            .elementByCss('#level1-home-page p').text()

        expect(text).toBe('This is the Level1 home page')
        browser.close()
      })

      it('should render the about page', async () => {
        const browser = await webdriver(context.port, '/')
        const text = await browser
            .elementByCss('#level1-about-page').click()
            .waitForElementByCss('#level1-about-page')
            .elementByCss('#level1-about-page p').text()

        expect(text).toBe('This is the Level1 about page')
        browser.close()
      })
    })
  })
}
<|endoftext|>


<commit_before>var path = require( "path" ),
	tmx = require("tmx-parser");

module.exports = function () {
	return function ( resource, next ) {

		if ( !resource.data || !resource.isXml || !resource.data.children[ 0 ].getElementsByTagName("tileset") ) {
			return next();
		}

		var route = path.dirname( resource.url.replace( this.baseUrl, '' ) );

		var loadOptions = {
			crossOrigin: resource.crossOrigin,
			loadType: PIXI.loaders.Resource.LOAD_TYPE.IMAGE
		};

		var that = this;

		tmx.parse(resource.xhr.responseText, route, function(err, map) {
			if (err) throw err;

			map.tileSets.forEach( function ( tileset ) {
				this.add( tileset.image.source , route + '/' + tileset.image.source, loadOptions );
			}, that);

			resource.data = map;
			next();
		});
	};
};<commit_msg>fixed multiple instances of an image not loading<commit_after>var path = require( "path" ),
	tmx = require("tmx-parser");

module.exports = function () {
	return function ( resource, next ) {

		if ( !resource.data || !resource.isXml || !resource.data.children[ 0 ].getElementsByTagName("tileset") ) {
			return next();
		}

		var route = path.dirname( resource.url.replace( this.baseUrl, '' ) );

		var loadOptions = {
			crossOrigin: resource.crossOrigin,
			loadType: PIXI.loaders.Resource.LOAD_TYPE.IMAGE
		};

		var that = this;

		tmx.parse(resource.xhr.responseText, route, function(err, map) {
			if (err) throw err;

			map.tileSets.forEach( function ( tileset ) {
				if ( !(tileset.image.source in this.resources) ) {
					this.add( tileset.image.source , route + '/' + tileset.image.source, loadOptions );
				}
			}, that);

			resource.data = map;
			next();
		});
	};
};<|endoftext|>


<commit_before>/**
 * document.ready currently sponsored by snipplr.com :)
 * @copyright http://snipplr.com/view/6156/documentready/
 */
(function () {
    var ie = !!(window.attachEvent && !window.opera);
    var wk = /webkit\/(\d+)/i.test(navigator.userAgent) && (RegExp.$1 < 525);
    var fn = [];
    var run = function () {
        for (var i = 0; i < fn.length; i++) fn[i]();
    };
    var d = document;
    d.ready = function (f) {
        if (!ie && !wk && d.addEventListener)
            return d.addEventListener('DOMContentLoaded', f, false);
        if (fn.push(f) > 1) return;
        if (ie)
            (function () {
                try {
                    d.documentElement.doScroll('left');
                    run();
                }
                catch (err) {
                    setTimeout(arguments.callee, 0);
                }
            })();
        else if (wk)
            var t = setInterval(function () {
                if (/^(loaded|complete)$/.test(d.readyState))
                    clearInterval(t),run();
            }, 0);
    };
})();

//@TODO implement own ready-method

/**
 * just for testing-purpose
 */
DEBUG_LEVEL = true;
debug = function(arg) {
    if (DEBUG_LEVEL && console != undefined)
        console.info(arg);
};

/**
 * Cyricles
 *
 * @license see LICENSE file
 * @copyright Florian Purchess <florian@purchess.de>
 *
 * ####################################################################################
 */

/**
 * Initalize a new Cyricles-Object
 * @param canvas
 * @param options
 */
Cyricles = function (canvas, options) {
    var type = typeof(canvas);

    if (type == "string")
        this.canvas = document.getElementById(canvas);
    else if (type == "object")
        this.canvas = canvas;
    else
        return null;

    this.ctx = this.canvas.getContext("2d");

    // set up options
    this.options = Cyricles.extend({
            renderInterval : 20,
            background : 'transparent',
            width : 800,
            height : 600
        }, options);

    // init object-stack
    this.items = new Array();

    this.renderingInterval = null;
};

Cyricles.extend = function(defaults, options) {
    //@TODO Secure extending + extending of Cyricles
    if (options == undefined) return defaults;

    for(var i in defaults) {
        if (options[i] == undefined)
            options[i] = defaults[i];
    }

    return options;
};

Cyricles.prototype.loadImages = function(images, callback) {
    var load = images.length; // amount of load images

    for(var i=0; i < images.length; i++) {
        var img = new Image();
        img.onload = img.onabort = img.onerror = function() {load--;};
        img.src = images[i];
        images[i] = img;
    }

    var t = setInterval(function() {
        if (!load) {
            clearInterval(t);
            if (typeof(callback) == "function")
                callback(images);
        }
    }, 0);

    return images;
};

/**
 * clear the canvas
 */
Cyricles.prototype.clearScreen = function() {
    this.canvas.width = this.canvas.width;
    if (this.options.background == 'transparent') return;

    this.ctx.save();
    this.ctx.fillStyle = this.options.background;
    this.ctx.fillRect(0,0,this.canvas.width, this.canvas.height);
    this.ctx.restore();
};

/**
 * render the current rendering-stack
 */
Cyricles.prototype.render = function() {
    this.clearScreen();

    for (var i=0; i < this.items.length; i++) {
        if (typeof(this.items[i]) == "function")
            this.items[i](this);
        else
            this.items[i].draw(this.ctx);
    }
};

/**
 * start rendering-interval
 */
Cyricles.prototype.startRender = function(){
    if (this.isRendering()) return false;

    this.render.call(this);
    var scope = this;
    this.renderingInterval = setInterval(function(){scope.render.call(scope)}, this.options.renderInterval);

    return true;
};

/**
 * stop rendering-interval
 */
Cyricles.prototype.stopRender = function(){
    if (!this.isRendering()) return false;

    clearInterval(this.renderingInterval);
    this.renderingInterval = null;

    return true;
};

/**
 * return current rendering-status
 */
Cyricles.prototype.isRendering = function(){
    return this.renderingInterval !== null;
};

/**
 * Add an item to the rendering-stack
 * @param item
 */
Cyricles.prototype.addItem = function(item, zOrder) {
    if (zOrder == undefined) {
        if (item.zOrder == undefined) {
            var zOrder = 0;

            if (this.items.length)
                zOrder = this.items[this.items.length - 1].zOrder + 1;

            item.zOrder = zOrder;
        }
    } else {
        item.zOrder = zOrder;
    }

    this.items.push(item);
    this.sortStack();
};

/**
 * Remove an item off the rendering-stack
 * @param index
 */
Cyricles.prototype.removeItem = function(index) {
    if (index == undefined)
        this.items.pop();
    else
        delete this.items[index];
};

/**
 * Bring the rendering-stack into the right order (zOrder)
 */
Cyricles.prototype.sortStack = function() {
    this.items.sort(function(a,b){return a.zOrder - b.zOrder;})
};

/**
 * Convert a string containing color-informations into an animatable object
 * @param colorString
 */
Cyricles.prototype.colorToObject = function(colorString) {
    var color = {R:null, G:null, B:null, Alpha: 255};

    if (colorString.charAt(0) == '#') {
        // HEX
        color.R = parseInt(colorString.substring(1,3), 16);
        color.G = parseInt(colorString.substring(3,5), 16);
        color.B = parseInt(colorString.substring(5,7), 16);

        // parse alpha
        if (colorString.length == 9) {
            color.Alpha = parseInt(colorString.substring(7,9), 16);
        }

    } else if ( colMatch = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/gi.exec(colorString) ) {
        // RGB
        color.R = colMatch[1];
        color.G = colMatch[2];
        color.B = colMatch[3];

    } else if ( colMatch = /^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(\d{1,3})\)$/gi.exec(colorString) ) {
        // RGBA
        color.R = colMatch[1];
        color.G = colMatch[2];
        color.B = colMatch[3];
        color.Alpha = colMatch[4];
    }

    return color;
};

/**
 * CyObject
 * ####################################################################################
 */

/**
 * Initalize a CyObject
 * @param type
 */
CyObject = function(type) {
    this.type = type;
    this.zOrder = 0;
};

/**
 * Set drawing-attributes
 * @param ctx
 */
CyObject.prototype.setDrawingAttributes = function(ctx){
    for (var name in this.options)
        ctx[name] = this.options[name];

};

/**
 * Retrieve all options that will be animated
 * @param user
 * @param options
 */
CyObject.prototype.getAnimatables = function(user, options) {
    var animatables = {}, key;

    if (options == undefined)
        options = this.options;

    for(key in user) {
        if (options[key] != undefined) {
            if (typeof(user[key]) == "object")
                animatables[key] = this.getAnimatables(user[key], options[key]);
            else
                animatables[key] = user[key];
        }
    }

    return animatables;
};

/**
 * Get the increase-value <addifier> for every <animatable> in relation to <duration> / <steps>
 * @param animatable
 * @param duration
 * @param steps
 * @param options
 */
CyObject.prototype.getAddifiers = function(animatable, duration, steps, options) {
    var addifiers = {}, key;

    if (options == undefined)
        options = this.options;

    for(key in animatable) {
        if (options[key] != undefined) {
            if (typeof(animatable[key]) == "object")
                addifiers[key] = this.getAddifiers(animatable[key], duration, steps, options[key]);
            else if (typeof(animatable[key]) == "number") {
                addifiers[key] = (animatable[key] - options[key]) / (duration / steps);
            }
        }
    }

    return addifiers;
};

/**
 * Add the values of <addifiers> to the values of <options>
 * @param addifiers
 * @param options
 */
CyObject.prototype.addOptionValues = function(addifiers, options) {
    var key;

    if (options == undefined)
        options = this.options;

    for(key in addifiers) {
        if (options[key] != undefined) {
            if (typeof(addifiers[key]) == "object")
                options[key] = this.addOptionValues(addifiers[key], options[key]);
            else if (typeof(addifiers[key]) == "number")
                options[key] += addifiers[key];
        }
    }

    return options;
};

/**
 * Executes a function <fn> for <duration> (in <steps> ms), passing <parameters>, executing callback at the end
 * @param fn
 * @param duration
 * @param steps
 * @param parameters
 * @param callback
 */
CyObject.prototype.timer = function(fn, duration, steps, parameters, callback) {
    if (duration < steps)
        throw("timer duration < steps!");

    var i = setInterval(function(fn, parameters){
        fn(parameters);

        if ((duration -= steps) <= 0)
            clearInterval(i),callback();
    }, steps, fn, parameters);

};

/**
 * Animates CyObject's options until values of <options> over duration, calling <callback> at the end
 * @param options
 * @param duration
 * @param callback
 */
CyObject.prototype.animate = function(options, duration, callback) {
    if (callback == undefined)
        callback = function(){};

    var animatables = this.getAnimatables(options);
    var addifiers = this.getAddifiers(animatables, duration, 1);

    this.timer(function(that){
        that.options = that.addOptionValues(addifiers);
    }, duration, 1, this, callback);
};

/**
 * CyTransform
 * ####################################################################################
 */

/**
 * Initalize a transformation-object
 * @param options
 */
CyTransformation = function(options) {
    this.options = Cyricles.extend({scale:[1,1], rotate:0, translate:[0,0], transform:[0, 0, 0, 0, 0, 0]}, options);
};

/**
 * Inheritance of CyObject
 */
CyTransformation.prototype = new CyObject("CyTransformation");
CyTransformation.prototype.constructor = CyTransformation;

/**
 * apply a scale to a context
 * @param ctx
 */
CyTransformation.prototype.draw = function(ctx) {
    ctx.scale(this.options.scale[0],this.options.scale[1]);
    ctx.rotate(this.options.rotate);
    ctx.translate(this.options.translate[0], this.options.translate[1]);
//    ctx.transform(this.options.transform[0], this.options.transform[1], this.options.transform[2],
//            this.options.transform[3], this.options.transform[4], this.options.transform[5]);
};

/**
 * CyRect
 * ####################################################################################
 */

/**
 * Initalize a new Cyricles-Rectangle
 *
 * @param x
 * @param y
 * @param width
 * @param height
 * @param options
 */
CyRect = function(x, y, width, height, options) {
    this.options = Cyricles.extend({
        strokeStyle: false,
        fillStyle: false
    }, options);

    this.options.x = x;
    this.options.y = y;
    this.options.width = width;
    this.options.height = height;
};

/**
 * Inheritance of CyObject
 */
CyRect.prototype = new CyObject("CyRect");
CyRect.prototype.constructor = CyRect;

/**
 * draw rectangle on the canvas-context
 * @param ctx
 */
CyRect.prototype.draw = function(ctx){
    ctx.save();

    this.setDrawingAttributes(ctx);

    if (this.options.fillStyle !== false)
        ctx.fillRect(this.options.x, this.options.y, this.options.width, this.options.height);

    if (this.options.strokeStyle !== false)
        ctx.strokeRect(this.options.x, this.options.y, this.options.width, this.options.height);

    ctx.restore();
};

//@TODO implement animation-algorithm for colors

/**
 * CyArc
 * ####################################################################################
 */

/**
 * Initalize a new Cyricles-Rectangle
 *
 * @param x
 * @param y
 * @param options
 */
CyArc = function(x, y, radius, options) {
    this.options = Cyricles.extend({
        strokeStyle: '#000',
        fillStyle: '#fff',
        startAngle: 0,
        endAngle: Math.PI*2,
        anticlockwise: false
    }, options);

    this.options.x = x;
    this.options.y = y;
    this.options.radius = radius;
};

/**
 * Inheritance of CyObject
 */
CyArc.prototype = new CyObject("CyArc");
CyArc.prototype.constructor = CyArc;

/**
 * draw rectangle on the canvas-context
 * @param ctx
 */
CyArc.prototype.draw = function(ctx){
    ctx.save();

    this.setDrawingAttributes(ctx);

    ctx.beginPath();
    ctx.arc(this.options.x, this.options.y, this.options.radius, this.options.startAngle, this.options.endAngle, this.options.anticlockwise);
    ctx.closePath();

    if (this.options.strokeStyle)
        ctx.stroke();
    if (this.options.fillStyle)
        ctx.fill();

    ctx.restore();
};

/**
 * CyText
 * ####################################################################################
 */

/**
 * Initalize a new CyText-Object
 * @param text
 * @param x
 * @param y
 * @param options
 */
CyText = function(text, x, y, options) {
    this.options = Cyricles.extend({
        font: "10px Arial",
        textAlign: "start",
        fillStyle: "#000"
    }, options);

    this.text = text;
    this.options.x = x;
    this.options.y = y;
};

/**
 * Inheritance of CyObject
 */
CyText.prototype = new CyObject("CyText");
CyText.prototype.constructor = CyText;

/**
 * draw text on the canvas-context
 * @param ctx
 */
CyText.prototype.draw = function(ctx){
    ctx.save();

    this.setDrawingAttributes(ctx);
    ctx.fillText(this.text, this.options.x, this.options.y);

    ctx.restore();
};

//@TODO implement text-animations

//@TODO implement some gradient-handling<commit_msg>fixed implicit declaration of colMatch<commit_after>/**
 * document.ready currently sponsored by snipplr.com :)
 * @copyright http://snipplr.com/view/6156/documentready/
 */
(function () {
    var ie = !!(window.attachEvent && !window.opera);
    var wk = /webkit\/(\d+)/i.test(navigator.userAgent) && (RegExp.$1 < 525);
    var fn = [];
    var run = function () {
        for (var i = 0; i < fn.length; i++) fn[i]();
    };
    var d = document;
    d.ready = function (f) {
        if (!ie && !wk && d.addEventListener)
            return d.addEventListener('DOMContentLoaded', f, false);
        if (fn.push(f) > 1) return;
        if (ie)
            (function () {
                try {
                    d.documentElement.doScroll('left');
                    run();
                }
                catch (err) {
                    setTimeout(arguments.callee, 0);
                }
            })();
        else if (wk)
            var t = setInterval(function () {
                if (/^(loaded|complete)$/.test(d.readyState))
                    clearInterval(t),run();
            }, 0);
    };
})();

//@TODO implement own ready-method

/**
 * just for testing-purpose
 */
DEBUG_LEVEL = true;
debug = function(arg) {
    if (DEBUG_LEVEL && console != undefined)
        console.info(arg);
};

/**
 * Cyricles
 *
 * @license see LICENSE file
 * @copyright Florian Purchess <florian@purchess.de>
 *
 * ####################################################################################
 */

/**
 * Initalize a new Cyricles-Object
 * @param canvas
 * @param options
 */
Cyricles = function (canvas, options) {
    var type = typeof(canvas);

    if (type == "string")
        this.canvas = document.getElementById(canvas);
    else if (type == "object")
        this.canvas = canvas;
    else
        return null;

    this.ctx = this.canvas.getContext("2d");

    // set up options
    this.options = Cyricles.extend({
            renderInterval : 20,
            background : 'transparent',
            width : 800,
            height : 600
        }, options);

    // init object-stack
    this.items = new Array();

    this.renderingInterval = null;
};

Cyricles.extend = function(defaults, options) {
    //@TODO Secure extending + extending of Cyricles
    if (options == undefined) return defaults;

    for(var i in defaults) {
        if (options[i] == undefined)
            options[i] = defaults[i];
    }

    return options;
};

Cyricles.prototype.loadImages = function(images, callback) {
    var load = images.length; // amount of load images

    for(var i=0; i < images.length; i++) {
        var img = new Image();
        img.onload = img.onabort = img.onerror = function() {load--;};
        img.src = images[i];
        images[i] = img;
    }

    var t = setInterval(function() {
        if (!load) {
            clearInterval(t);
            if (typeof(callback) == "function")
                callback(images);
        }
    }, 0);

    return images;
};

/**
 * clear the canvas
 */
Cyricles.prototype.clearScreen = function() {
    this.canvas.width = this.canvas.width;
    if (this.options.background == 'transparent') return;

    this.ctx.save();
    this.ctx.fillStyle = this.options.background;
    this.ctx.fillRect(0,0,this.canvas.width, this.canvas.height);
    this.ctx.restore();
};

/**
 * render the current rendering-stack
 */
Cyricles.prototype.render = function() {
    this.clearScreen();

    for (var i=0; i < this.items.length; i++) {
        if (typeof(this.items[i]) == "function")
            this.items[i](this);
        else
            this.items[i].draw(this.ctx);
    }
};

/**
 * start rendering-interval
 */
Cyricles.prototype.startRender = function(){
    if (this.isRendering()) return false;

    this.render.call(this);
    var scope = this;
    this.renderingInterval = setInterval(function(){scope.render.call(scope)}, this.options.renderInterval);

    return true;
};

/**
 * stop rendering-interval
 */
Cyricles.prototype.stopRender = function(){
    if (!this.isRendering()) return false;

    clearInterval(this.renderingInterval);
    this.renderingInterval = null;

    return true;
};

/**
 * return current rendering-status
 */
Cyricles.prototype.isRendering = function(){
    return this.renderingInterval !== null;
};

/**
 * Add an item to the rendering-stack
 * @param item
 */
Cyricles.prototype.addItem = function(item, zOrder) {
    if (zOrder == undefined) {
        if (item.zOrder == undefined) {
            var zOrder = 0;

            if (this.items.length)
                zOrder = this.items[this.items.length - 1].zOrder + 1;

            item.zOrder = zOrder;
        }
    } else {
        item.zOrder = zOrder;
    }

    this.items.push(item);
    this.sortStack();
};

/**
 * Remove an item off the rendering-stack
 * @param index
 */
Cyricles.prototype.removeItem = function(index) {
    if (index == undefined)
        this.items.pop();
    else
        delete this.items[index];
};

/**
 * Bring the rendering-stack into the right order (zOrder)
 */
Cyricles.prototype.sortStack = function() {
    this.items.sort(function(a,b){return a.zOrder - b.zOrder;})
};

/**
 * Convert a string containing color-informations into an animatable object
 * @param colorString
 */
Cyricles.prototype.colorToObject = function(colorString) {
    var color = {R:null, G:null, B:null, Alpha: 255}, colMatch;

    if (colorString.charAt(0) == '#') {
        // HEX
        color.R = parseInt(colorString.substring(1,3), 16);
        color.G = parseInt(colorString.substring(3,5), 16);
        color.B = parseInt(colorString.substring(5,7), 16);

        // parse alpha
        if (colorString.length == 9) {
            color.Alpha = parseInt(colorString.substring(7,9), 16);
        }

    } else if ( colMatch = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/gi.exec(colorString) ) {
        // RGB
        color.R = colMatch[1];
        color.G = colMatch[2];
        color.B = colMatch[3];

    } else if ( colMatch = /^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(\d{1,3})\)$/gi.exec(colorString) ) {
        // RGBA
        color.R = colMatch[1];
        color.G = colMatch[2];
        color.B = colMatch[3];
        color.Alpha = colMatch[4];
    }

    return color;
};

/**
 * CyObject
 * ####################################################################################
 */

/**
 * Initalize a CyObject
 * @param type
 */
CyObject = function(type) {
    this.type = type;
    this.zOrder = 0;
};

/**
 * Set drawing-attributes
 * @param ctx
 */
CyObject.prototype.setDrawingAttributes = function(ctx){
    for (var name in this.options)
        ctx[name] = this.options[name];

};

/**
 * Retrieve all options that will be animated
 * @param user
 * @param options
 */
CyObject.prototype.getAnimatables = function(user, options) {
    var animatables = {}, key;

    if (options == undefined)
        options = this.options;

    for(key in user) {
        if (options[key] != undefined) {
            if (typeof(user[key]) == "object")
                animatables[key] = this.getAnimatables(user[key], options[key]);
            else
                animatables[key] = user[key];
        }
    }

    return animatables;
};

/**
 * Get the increase-value <addifier> for every <animatable> in relation to <duration> / <steps>
 * @param animatable
 * @param duration
 * @param steps
 * @param options
 */
CyObject.prototype.getAddifiers = function(animatable, duration, steps, options) {
    var addifiers = {}, key;

    if (options == undefined)
        options = this.options;

    for(key in animatable) {
        if (options[key] != undefined) {
            if (typeof(animatable[key]) == "object")
                addifiers[key] = this.getAddifiers(animatable[key], duration, steps, options[key]);
            else if (typeof(animatable[key]) == "number") {
                addifiers[key] = (animatable[key] - options[key]) / (duration / steps);
            }
        }
    }

    return addifiers;
};

/**
 * Add the values of <addifiers> to the values of <options>
 * @param addifiers
 * @param options
 */
CyObject.prototype.addOptionValues = function(addifiers, options) {
    var key;

    if (options == undefined)
        options = this.options;

    for(key in addifiers) {
        if (options[key] != undefined) {
            if (typeof(addifiers[key]) == "object")
                options[key] = this.addOptionValues(addifiers[key], options[key]);
            else if (typeof(addifiers[key]) == "number")
                options[key] += addifiers[key];
        }
    }

    return options;
};

/**
 * Executes a function <fn> for <duration> (in <steps> ms), passing <parameters>, executing callback at the end
 * @param fn
 * @param duration
 * @param steps
 * @param parameters
 * @param callback
 */
CyObject.prototype.timer = function(fn, duration, steps, parameters, callback) {
    if (duration < steps)
        throw("timer duration < steps!");

    var i = setInterval(function(fn, parameters){
        fn(parameters);

        if ((duration -= steps) <= 0)
            clearInterval(i),callback();
    }, steps, fn, parameters);

};

/**
 * Animates CyObject's options until values of <options> over duration, calling <callback> at the end
 * @param options
 * @param duration
 * @param callback
 */
CyObject.prototype.animate = function(options, duration, callback) {
    if (callback == undefined)
        callback = function(){};

    var animatables = this.getAnimatables(options);
    var addifiers = this.getAddifiers(animatables, duration, 1);

    this.timer(function(that){
        that.options = that.addOptionValues(addifiers);
    }, duration, 1, this, callback);
};

/**
 * CyTransform
 * ####################################################################################
 */

/**
 * Initalize a transformation-object
 * @param options
 */
CyTransformation = function(options) {
    this.options = Cyricles.extend({scale:[1,1], rotate:0, translate:[0,0], transform:[0, 0, 0, 0, 0, 0]}, options);
};

/**
 * Inheritance of CyObject
 */
CyTransformation.prototype = new CyObject("CyTransformation");
CyTransformation.prototype.constructor = CyTransformation;

/**
 * apply a scale to a context
 * @param ctx
 */
CyTransformation.prototype.draw = function(ctx) {
    ctx.scale(this.options.scale[0],this.options.scale[1]);
    ctx.rotate(this.options.rotate);
    ctx.translate(this.options.translate[0], this.options.translate[1]);
//    ctx.transform(this.options.transform[0], this.options.transform[1], this.options.transform[2],
//            this.options.transform[3], this.options.transform[4], this.options.transform[5]);
};

/**
 * CyRect
 * ####################################################################################
 */

/**
 * Initalize a new Cyricles-Rectangle
 *
 * @param x
 * @param y
 * @param width
 * @param height
 * @param options
 */
CyRect = function(x, y, width, height, options) {
    this.options = Cyricles.extend({
        strokeStyle: false,
        fillStyle: false
    }, options);

    this.options.x = x;
    this.options.y = y;
    this.options.width = width;
    this.options.height = height;
};

/**
 * Inheritance of CyObject
 */
CyRect.prototype = new CyObject("CyRect");
CyRect.prototype.constructor = CyRect;

/**
 * draw rectangle on the canvas-context
 * @param ctx
 */
CyRect.prototype.draw = function(ctx){
    ctx.save();

    this.setDrawingAttributes(ctx);

    if (this.options.fillStyle !== false)
        ctx.fillRect(this.options.x, this.options.y, this.options.width, this.options.height);

    if (this.options.strokeStyle !== false)
        ctx.strokeRect(this.options.x, this.options.y, this.options.width, this.options.height);

    ctx.restore();
};

//@TODO implement animation-algorithm for colors

/**
 * CyArc
 * ####################################################################################
 */

/**
 * Initalize a new Cyricles-Rectangle
 *
 * @param x
 * @param y
 * @param options
 */
CyArc = function(x, y, radius, options) {
    this.options = Cyricles.extend({
        strokeStyle: '#000',
        fillStyle: '#fff',
        startAngle: 0,
        endAngle: Math.PI*2,
        anticlockwise: false
    }, options);

    this.options.x = x;
    this.options.y = y;
    this.options.radius = radius;
};

/**
 * Inheritance of CyObject
 */
CyArc.prototype = new CyObject("CyArc");
CyArc.prototype.constructor = CyArc;

/**
 * draw rectangle on the canvas-context
 * @param ctx
 */
CyArc.prototype.draw = function(ctx){
    ctx.save();

    this.setDrawingAttributes(ctx);

    ctx.beginPath();
    ctx.arc(this.options.x, this.options.y, this.options.radius, this.options.startAngle, this.options.endAngle, this.options.anticlockwise);
    ctx.closePath();

    if (this.options.strokeStyle)
        ctx.stroke();
    if (this.options.fillStyle)
        ctx.fill();

    ctx.restore();
};

/**
 * CyText
 * ####################################################################################
 */

/**
 * Initalize a new CyText-Object
 * @param text
 * @param x
 * @param y
 * @param options
 */
CyText = function(text, x, y, options) {
    this.options = Cyricles.extend({
        font: "10px Arial",
        textAlign: "start",
        fillStyle: "#000"
    }, options);

    this.text = text;
    this.options.x = x;
    this.options.y = y;
};

/**
 * Inheritance of CyObject
 */
CyText.prototype = new CyObject("CyText");
CyText.prototype.constructor = CyText;

/**
 * draw text on the canvas-context
 * @param ctx
 */
CyText.prototype.draw = function(ctx){
    ctx.save();

    this.setDrawingAttributes(ctx);
    ctx.fillText(this.text, this.options.x, this.options.y);

    ctx.restore();
};

//@TODO implement text-animations

//@TODO implement some gradient-handling<|endoftext|>


<commit_before>'use strict';

// Make sure to call log-setup before requiring any other sources to make sure
// that the configuration applies to any loggers initialized in module scope.
require('../../../lib/cli/log-setup')();

var fs = require('fs');
var util = require('util');
var path = require('path');
var _ = require('underscore');
var Promise = require('bluebird');
var expect = require('chai').expect;
var JuttleLogger = require('../../../lib/logger');
var utils = require('../../../lib/runtime').utils;
var adapters = require('../../../lib/runtime/adapters');
var JuttleMoment = require('../../../lib/runtime/types/juttle-moment');
var compiler = require('../../../lib/compiler');
var Scheduler = require('../../../lib/runtime/scheduler').Scheduler;
var TestScheduler = require('../../../lib/runtime/scheduler').TestScheduler;
var implicit_views = require('../../../lib/compiler/flowgraph/implicit_views')();
var optimize = require('../../../lib/compiler/optimize');
var View = require('../../../lib/views/view.js');

// Configure the test adapter
adapters.configure({
    test: {
        path: path.resolve(__dirname, '../test-adapter')
    },
    testTimeseries: {
        path: path.resolve(__dirname, '../test-adapter-timeseries')
    }
});

var logger = JuttleLogger.getLogger('juttle-tests');
logger.debug('initializing');

// silence the deprecation warnings from this:
//      https://github.com/moment/moment/issues/1407
// which are really only being triggered when we're doing negative testing
var moment = require('moment');
moment.createFromInputFallback = function(config) {
    // unreliable string magic, or
    config._d = new Date(config._i);
};

// a bunch of tests are expecting to get epochms from Juttle,
// but now we only return ISO strings. This utility converts
// them back into epochms for the relevant tests.
function get_times(pts) {
    _.each(utils.toNative(pts), function(pt) {
        if (pt.time instanceof JuttleMoment) {
            pt.time = pt.time.valueOf();
        }
    });

    return pts;
}

(function() {
    var FileAdapter = require('../../../lib/adapters/file')();
    _(FileAdapter.read.prototype).extend({
        fetch: function() {
            var filepath = path.resolve(__dirname, this.filename);
            return fs.createReadStream(filepath, { encoding: 'utf8' });
        }
    });
})();

var stdin = process.stdin;
var stdout = process.stdout;

function set_stdin(stream) {
    stdin = stream;
}

function set_stdout(stream) {
    stdout = stream;
}

// allow for easy injection of input stream for the `read stdin` adapter
// at test time
(function() {
    var stdio = require('../../../lib/adapters/stdio')();
    _(stdio.read.prototype).extend({
        getStdin: function() {
            return stdin;
        }
    });
    _(stdio.write.prototype).extend({
        getStdout: function() {
            return stdout;
        }
    });
})();

class TestView extends View {
    constructor(options, env) {
        super(options, env);
        this.name = 'test';
        this.sink = options.sink;
        this.eofs = options.eofs || 1;
        this.data = [];
        this.ticks = options.sink.options && options.sink.options.ticks;
        this.marks = options.sink.options && options.sink.options.marks;
        this.times = options.sink.options && options.sink.options.times;
        this.dt = options.sink.options && options.sink.options.dt;
        this.env = env;
    }
    consume(data) {
        this.data = this.data.concat(utils.fromNative(data.map(_.clone)));
    }
    mark(time) {
        if (this.marks && this.times) {
            this.data = this.data.concat(utils.fromNative([{time:time, mark:true}]));
        } else if (this.marks) {
            this.data = this.data.concat({mark:true});
        }
    }
    tick(time) {
        if (this.ticks) {
            if (this.times) {
                this.data = this.data.concat(utils.fromNative([{time:time, tick:true}]));
            } else if (this.dt) {
                this.data = this.data.concat(utils.fromNative([{dt:JuttleMoment.subtract(time, this.env.now), tick:true}]));
            } else {
                this.data = this.data.concat({tick:true});
            }
        }
    }
    eof() {
        if (--this.eofs === 0) {
            var result = {};
            result.data = utils.fromNative(this.data);
            result.name = this.sink.name;

            result.type = this.sink.name;
            result.options = this.sink.options;
            logger.debug('End of file reached: emitting', result);
            this.events.emit('end', result);
        }
    }
}

function compile_juttle(options) {
    logger.debug('parsing Juttle program:', options.program);
    if (options.hasOwnProperty('modules') && options.hasOwnProperty('moduleResolver')) {
        throw new Error('confused by presence of both \'modules\' and \'moduleResolver\'');
    }
    var source = options.program;
    options = _.omit(options, 'program');

    var compile_opts = {
        modules: options.modules,
        moduleResolver: options.moduleResolver,
        fg_processors: [implicit_views, optimize],
        scheduler: options.realtime ? new Scheduler() : new TestScheduler()
    };

    if (_.has(options, 'inputs')) {
        var flowgraph = compiler.compileSync(source, {
            modules: options.modules,
            moduleResolver: options.moduleResolver,
            stage: 'flowgraph'
        }).built_graph;
        var bnames_build = _.map(flowgraph.inputs, function(input) { return input.id; });
        var bnames_test = _.keys(options.inputs);

        // all test-supplied bnames are valid build-computed bnames
        expect(_.union(bnames_build, bnames_test).length).to.equal(bnames_build.length);

        compile_opts.inputs = options.inputs;
    }

    if (_.has(options, 'input_defaults')) {
        compile_opts.input_defaults = options.input_defaults;
    }

    return compiler.compile(source, compile_opts);
}

function check_juttle(options, deactivateAfter) {
    /*
     * deactivateAfter: number of milliseconds to wait before deactivating the
     *                  program allowing for live/long running program testing
     */
    return compile_juttle(options)
    .then(function(prog) {
        var done = run_juttle(prog, options);

        if (deactivateAfter) {
            return Promise
            .delay(null, deactivateAfter)
            .then(function() {
                prog.deactivate();
                return done;
            });
        } else {
            return done;
        }
    });
}

function run_juttle(prog, options) {
    options = options || {};
    var graph = prog.graph;
    // make sure paths relative to the location of this file work:
    if (options.expect_file) {
        options.expect_file = path.resolve(__dirname, options.expect_file);
    }

    var expect_data = options.expect_data;
    var raw_data;
    var sink_options = {};

    if (options.sink_options) {
        sink_options = options.sink_options;
    }

    if (options.expect_file && ! options.write_expect_file) {
        raw_data = fs.readFileSync(options.expect_file, 'utf8');
        expect_data = JSON.parse(raw_data);
    }

    return Promise.try(function() {
        var views = {};

        var promises = _.map(prog.get_sinks(), function(sink) {
            // non-visual sinks don't implement pub/sub channel but instead have
            // a done() promise to indicate when they are at eof.
            if (sink.procName() !== 'view') {
                return sink.isDone
                    .then(function() {
                        return sink;
                    });
            } else {
                var view = new TestView(_.extend({ sink: sink }, sink_options[sink.name]), prog.env);
                views[sink.channel] = view;

                return new Promise(function(resolve, reject) {
                    view.events.on('end', resolve);
                    view.events.on('error', reject);
                });
            }
        });

        var errors = [];
        var warnings = [];

        // Dispatch to correct test view
        prog.events.on('view:mark', function(data) {
            views[data.channel].mark(data.time);
        });

        prog.events.on('view:tick', function(data) {
            views[data.channel].tick(data.time);
        });

        prog.events.on('view:eof', function(data) {
            views[data.channel].eof();
        });

        prog.events.on('view:points', function(data) {
            views[data.channel].consume(data.points);
        });

        prog.events.on('error', function(err) {
            errors.push(err);
        });

        prog.events.on('warning', function(warn) {
            warnings.push(warn);
        });

        logger.debug('activating Juttle program');
        prog.activate();

        return Promise.all(promises).spread(function() {
            var all_sinks = {};
            _.each(arguments, function(sink) {
                // XXX should this be fatal?
                if (_.has(all_sinks, sink.name)) {
                    logger.error('Duplicate sink name:', sink.name);
                }
                all_sinks[sink.name] = sink.data;
            });

            if (options.write_expect_file) {
                fs.writeFileSync(options.expect_file, JSON.stringify(all_sinks, null, '  '));
            }
            else if (expect_data) {
                expect(all_sinks).to.deep.equal(expect_data);
            }
            return {sinks: all_sinks, prog:prog, graph: graph, errors: errors, warnings: warnings};
        });
    });
}

function wait_for_event(program, type, checker) {
    return new Promise(function(resolve, reject) {
        program.events.on(type, resolve);
    })
    .then(function(err) {
        checker(err);
    });
}

function module_resolver(modules) {
    return function(name) {
        var filename = __dirname + '/../../../lib/stdlib/' + name;
        if (fs.existsSync(filename)) {
            return Promise.resolve({ name: name, source: fs.readFileSync(filename, 'utf8') });
        }

        var module = modules[name];
        if (module) {
            return Promise.resolve({ name: name, source: module });
        }

        throw new Error('Unknown module: ' + name);
    };
}

function options_from_object(options) {
    function _option_is_moment(key) {
        return key === 'from' || key === 'to';
    }

    return _.reduce(options, function(memo, value, key) {
        var formatted_str;
        if (_option_is_moment(key)) {
            formatted_str = util.format(':%s:', value);
        } else if(typeof value === 'string') {
            formatted_str = util.format('"%s"', value);
        } else {
            formatted_str = value;
        }
        return memo + '-' + key + ' ' + formatted_str + ' ';
    }, '');
}

function expect_to_fail(promise, message) {
    return promise.throw(new Error('should have failed'))
        .catch(function(err) {
            expect(err.message).equal(message);
        });
}

// mocha utils

function checkForModule(name) {
    /*
     * check for required module dependency
     */
    try {
        require(name);
    } catch(err) {
        logger.warn('WARNING: skipping test due to unmet dependency on module "' +
                    name + '"');
        return false;
    }

    return true;
}

var mochaIt = global.it;

function withModuleIt(description, fn, module) {
    /*
     * wrapper function for mocha it() so you can skip a test based on module
     * availability.
     *
     *  module: the name of a module that must be require'able for a specific
     *          test to be executed or skipped if the module can not be
     *          required
     *
     * if you have the need to set the same requirements for all tests in a
     * single test suite then use the following approach and redefine it()
     * locally to that one file like so:
     *
     *  var withGrokIt = function(describe, function) {
     *      return require('../../runtime/specs/juttle-test-utils').it(describe, function, 'node-grok');
     *  };
     *
     */
    if (checkForModule(module)) {
        mochaIt(description, fn);
    } else {
        mochaIt.skip(description, fn);
    }
}

module.exports = {
    wait_for_event: wait_for_event,
    get_times: get_times,
    compile_juttle: compile_juttle,
    check_juttle: check_juttle,
    run_juttle: run_juttle,
    module_resolver: module_resolver,
    options_from_object: options_from_object,
    expect_to_fail: expect_to_fail,
    set_stdin: set_stdin,
    set_stdout: set_stdout,
    withModuleIt
};
<commit_msg>juttle-test-utils: add utilities for testing adapters<commit_after>'use strict';

// Make sure to call log-setup before requiring any other sources to make sure
// that the configuration applies to any loggers initialized in module scope.
require('../../../lib/cli/log-setup')();

var fs = require('fs');
var util = require('util');
var path = require('path');
var _ = require('underscore');
var Promise = require('bluebird');
var expect = require('chai').expect;
var JuttleLogger = require('../../../lib/logger');
var utils = require('../../../lib/runtime').utils;
var adapters = require('../../../lib/runtime/adapters');
var adapterAPI = require('../../../lib/adapters/api');
var JuttleMoment = require('../../../lib/runtime/types/juttle-moment');
var compiler = require('../../../lib/compiler');
var Scheduler = require('../../../lib/runtime/scheduler').Scheduler;
var TestScheduler = require('../../../lib/runtime/scheduler').TestScheduler;
var implicit_views = require('../../../lib/compiler/flowgraph/implicit_views')();
var optimize = require('../../../lib/compiler/optimize');
var View = require('../../../lib/views/view.js');

// Configure the test adapter
adapters.configure({
    test: {
        path: path.resolve(__dirname, '../test-adapter')
    },
    testTimeseries: {
        path: path.resolve(__dirname, '../test-adapter-timeseries')
    }
});

var logger = JuttleLogger.getLogger('juttle-tests');
logger.debug('initializing');

// silence the deprecation warnings from this:
//      https://github.com/moment/moment/issues/1407
// which are really only being triggered when we're doing negative testing
var moment = require('moment');
moment.createFromInputFallback = function(config) {
    // unreliable string magic, or
    config._d = new Date(config._i);
};

// a bunch of tests are expecting to get epochms from Juttle,
// but now we only return ISO strings. This utility converts
// them back into epochms for the relevant tests.
function get_times(pts) {
    _.each(utils.toNative(pts), function(pt) {
        if (pt.time instanceof JuttleMoment) {
            pt.time = pt.time.valueOf();
        }
    });

    return pts;
}

(function() {
    var FileAdapter = require('../../../lib/adapters/file')();
    _(FileAdapter.read.prototype).extend({
        fetch: function() {
            var filepath = path.resolve(__dirname, this.filename);
            return fs.createReadStream(filepath, { encoding: 'utf8' });
        }
    });
})();

var stdin = process.stdin;
var stdout = process.stdout;

function set_stdin(stream) {
    stdin = stream;
}

function set_stdout(stream) {
    stdout = stream;
}

// allow for easy injection of input stream for the `read stdin` adapter
// at test time
(function() {
    var stdio = require('../../../lib/adapters/stdio')();
    _(stdio.read.prototype).extend({
        getStdin: function() {
            return stdin;
        }
    });
    _(stdio.write.prototype).extend({
        getStdout: function() {
            return stdout;
        }
    });
})();

class TestView extends View {
    constructor(options, env) {
        super(options, env);
        this.name = 'test';
        this.sink = options.sink;
        this.eofs = options.eofs || 1;
        this.data = [];
        this.ticks = options.sink.options && options.sink.options.ticks;
        this.marks = options.sink.options && options.sink.options.marks;
        this.times = options.sink.options && options.sink.options.times;
        this.dt = options.sink.options && options.sink.options.dt;
        this.env = env;
    }
    consume(data) {
        this.data = this.data.concat(utils.fromNative(data.map(_.clone)));
    }
    mark(time) {
        if (this.marks && this.times) {
            this.data = this.data.concat(utils.fromNative([{time:time, mark:true}]));
        } else if (this.marks) {
            this.data = this.data.concat({mark:true});
        }
    }
    tick(time) {
        if (this.ticks) {
            if (this.times) {
                this.data = this.data.concat(utils.fromNative([{time:time, tick:true}]));
            } else if (this.dt) {
                this.data = this.data.concat(utils.fromNative([{dt:JuttleMoment.subtract(time, this.env.now), tick:true}]));
            } else {
                this.data = this.data.concat({tick:true});
            }
        }
    }
    eof() {
        if (--this.eofs === 0) {
            var result = {};
            result.data = utils.fromNative(this.data);
            result.name = this.sink.name;

            result.type = this.sink.name;
            result.options = this.sink.options;
            logger.debug('End of file reached: emitting', result);
            this.events.emit('end', result);
        }
    }
}

function compile_juttle(options) {
    logger.debug('parsing Juttle program:', options.program);
    if (options.hasOwnProperty('modules') && options.hasOwnProperty('moduleResolver')) {
        throw new Error('confused by presence of both \'modules\' and \'moduleResolver\'');
    }
    var source = options.program;
    options = _.omit(options, 'program');

    var compile_opts = {
        modules: options.modules,
        moduleResolver: options.moduleResolver,
        fg_processors: [implicit_views, optimize],
        scheduler: options.realtime ? new Scheduler() : new TestScheduler()
    };

    if (_.has(options, 'inputs')) {
        var flowgraph = compiler.compileSync(source, {
            modules: options.modules,
            moduleResolver: options.moduleResolver,
            stage: 'flowgraph'
        }).built_graph;
        var bnames_build = _.map(flowgraph.inputs, function(input) { return input.id; });
        var bnames_test = _.keys(options.inputs);

        // all test-supplied bnames are valid build-computed bnames
        expect(_.union(bnames_build, bnames_test).length).to.equal(bnames_build.length);

        compile_opts.inputs = options.inputs;
    }

    if (_.has(options, 'input_defaults')) {
        compile_opts.input_defaults = options.input_defaults;
    }

    return compiler.compile(source, compile_opts);
}

function check_juttle(options, deactivateAfter) {
    /*
     * deactivateAfter: number of milliseconds to wait before deactivating the
     *                  program allowing for live/long running program testing
     */
    return compile_juttle(options)
    .then(function(prog) {
        var done = run_juttle(prog, options);

        if (deactivateAfter) {
            return Promise
            .delay(null, deactivateAfter)
            .then(function() {
                prog.deactivate();
                return done;
            });
        } else {
            return done;
        }
    });
}

function run_juttle(prog, options) {
    options = options || {};
    var graph = prog.graph;
    // make sure paths relative to the location of this file work:
    if (options.expect_file) {
        options.expect_file = path.resolve(__dirname, options.expect_file);
    }

    var expect_data = options.expect_data;
    var raw_data;
    var sink_options = {};

    if (options.sink_options) {
        sink_options = options.sink_options;
    }

    if (options.expect_file && ! options.write_expect_file) {
        raw_data = fs.readFileSync(options.expect_file, 'utf8');
        expect_data = JSON.parse(raw_data);
    }

    return Promise.try(function() {
        var views = {};

        var promises = _.map(prog.get_sinks(), function(sink) {
            // non-visual sinks don't implement pub/sub channel but instead have
            // a done() promise to indicate when they are at eof.
            if (sink.procName() !== 'view') {
                return sink.isDone
                    .then(function() {
                        return sink;
                    });
            } else {
                var view = new TestView(_.extend({ sink: sink }, sink_options[sink.name]), prog.env);
                views[sink.channel] = view;

                return new Promise(function(resolve, reject) {
                    view.events.on('end', resolve);
                    view.events.on('error', reject);
                });
            }
        });

        var errors = [];
        var warnings = [];

        // Dispatch to correct test view
        prog.events.on('view:mark', function(data) {
            views[data.channel].mark(data.time);
        });

        prog.events.on('view:tick', function(data) {
            views[data.channel].tick(data.time);
        });

        prog.events.on('view:eof', function(data) {
            views[data.channel].eof();
        });

        prog.events.on('view:points', function(data) {
            views[data.channel].consume(data.points);
        });

        prog.events.on('error', function(err) {
            errors.push(err);
        });

        prog.events.on('warning', function(warn) {
            warnings.push(warn);
        });

        logger.debug('activating Juttle program');
        prog.activate();

        return Promise.all(promises).spread(function() {
            var all_sinks = {};
            _.each(arguments, function(sink) {
                // XXX should this be fatal?
                if (_.has(all_sinks, sink.name)) {
                    logger.error('Duplicate sink name:', sink.name);
                }
                all_sinks[sink.name] = sink.data;
            });

            if (options.write_expect_file) {
                fs.writeFileSync(options.expect_file, JSON.stringify(all_sinks, null, '  '));
            }
            else if (expect_data) {
                expect(all_sinks).to.deep.equal(expect_data);
            }
            return {sinks: all_sinks, prog:prog, graph: graph, errors: errors, warnings: warnings};
        });
    });
}

function wait_for_event(program, type, checker) {
    return new Promise(function(resolve, reject) {
        program.events.on(type, resolve);
    })
    .then(function(err) {
        checker(err);
    });
}

function module_resolver(modules) {
    return function(name) {
        var filename = __dirname + '/../../../lib/stdlib/' + name;
        if (fs.existsSync(filename)) {
            return Promise.resolve({ name: name, source: fs.readFileSync(filename, 'utf8') });
        }

        var module = modules[name];
        if (module) {
            return Promise.resolve({ name: name, source: module });
        }

        throw new Error('Unknown module: ' + name);
    };
}

function options_from_object(options) {
    function _option_is_moment(key) {
        return key === 'from' || key === 'to';
    }

    return _.reduce(options, function(memo, value, key) {
        var formatted_str;
        if (_option_is_moment(key)) {
            formatted_str = util.format(':%s:', value);
        } else if(typeof value === 'string') {
            formatted_str = util.format('"%s"', value);
        } else {
            formatted_str = value;
        }
        return memo + '-' + key + ' ' + formatted_str + ' ';
    }, '');
}

function expect_to_fail(promise, message) {
    return promise.throw(new Error('should have failed'))
        .catch(function(err) {
            expect(err.message).equal(message);
        });
}

// mocha utils

function checkForModule(name) {
    /*
     * check for required module dependency
     */
    try {
        require(name);
    } catch(err) {
        logger.warn('WARNING: skipping test due to unmet dependency on module "' +
                    name + '"');
        return false;
    }

    return true;
}

var mochaIt = global.it;

function withModuleIt(description, fn, module) {
    /*
     * wrapper function for mocha it() so you can skip a test based on module
     * availability.
     *
     *  module: the name of a module that must be require'able for a specific
     *          test to be executed or skipped if the module can not be
     *          required
     *
     * if you have the need to set the same requirements for all tests in a
     * single test suite then use the following approach and redefine it()
     * locally to that one file like so:
     *
     *  var withGrokIt = function(describe, function) {
     *      return require('../../runtime/specs/juttle-test-utils').it(describe, function, 'node-grok');
     *  };
     *
     */
    if (checkForModule(module)) {
        mochaIt(description, fn);
    } else {
        mochaIt.skip(description, fn);
    }
}

// Wrapper around the adapter.configure method
function configureAdapter(config) {
    return adapters.configure(config);
}

// To facilitate unit testing of external juttle adapters, this wrapper function
// makes sure the the JuttleAdapterAPI global is  set during the execution of
// the specified function, which can be used to enclose a set of it() or describe()
// calls that need to pull in portions of the adapter code for tests.
function withAdapterAPI(fn) {
    global.JuttleAdapterAPI = adapterAPI;
    fn();
    global.JuttleAdapterAPI = undefined;
}

module.exports = {
    wait_for_event: wait_for_event,
    get_times: get_times,
    compile_juttle: compile_juttle,
    check_juttle: check_juttle,
    run_juttle: run_juttle,
    module_resolver: module_resolver,
    options_from_object: options_from_object,
    expect_to_fail: expect_to_fail,
    set_stdin: set_stdin,
    set_stdout: set_stdout,
    configureAdapter,
    withAdapterAPI,
    withModuleIt
};
<|endoftext|>


<commit_before>/**
	@namespace enyo
*/
(function (enyo, scope) {

	var kind = enyo.kind;

	/**
		All {@link enyo.Binding} instances are stored in this list and can be retrieved via the
		{@link enyo.Binding~find} method using a {@link enyo.Binding#id} to identify it.

		@public
		@name enyo.bindings
		@memberof enyo
	*/
	var bindings = enyo.bindings = [];

	var DIRTY_FROM = 0x01,
		DIRTY_TO = 0x02;

	/**
		Used to determine if a {@link enyo.Binding} is actually ready.

		@private
	*/
	function ready (binding) {
		var rdy = binding.ready;

		if (!rdy) {

			var from = binding.from || '',
				to = binding.to || '',
				source = binding.source,
				target = binding.target,
				owner = binding.owner,
				twoWay = !binding.oneWay,
				toTarget;

			if (typeof from != 'string') from = '';
			if (typeof to != 'string') to = '';

			if (!source) {

				// the worst case scenario here is for backward compatability purposes
				// we have to at least be able to derive the source via the from string
				if (from[0] == '^') {

					// this means we're reaching for a global
					from = from.slice(1);
					source = enyo.getPath.call(enyo.global, from);

				} else {
					source = owner;
				}

			}

			if (!target) {

				// same worst case as above, for backwards compatability purposes
				// we have to at least be able to derive the target via the to string
				if (to[0] == '^') {

					// this means we're reaching for a global
					to = to.slice(1);
					target = enyo.getPath.call(enyo.global, to);
				} else {
					target = owner;
				}
			}

			// we do this so we don't overwrite the originals in case we need to reset later
			binding._target = target;
			binding._source = source;
			binding._from = from[0] == '.'? from.slice(1): from;
			binding._to = to[0] == '.'? to.slice(1): to;

			if (!twoWay) {
				toTarget = binding._to.split('.');
				if (toTarget.length > 2) {
					toTarget.pop();
					binding._toTarget = toTarget.join('.');
				}
			}

			// now our sanitation
			rdy = !! (
				(source && (typeof source == 'object')) &&
				(target && (typeof source == 'object')) &&
				(from) &&
				(to)
			);
		}

		/*jshint -W093 */
		return (binding.ready = rdy);
		/*jshint +W093 */
	}

	/**
		@public
		@class enyo.Binding
	*/
	kind(
		/** @lends enyo.Binding.prototype */ {

		/**
			@private
		*/
		name: 'enyo.Binding',

		/**
			@private
		*/
		kind: null,

		/**
			@private
		*/
		noDefer: true,

		/**
			@public
		*/
		oneWay: true,

		/**
			@public
		*/
		connected: false,

		/**
			@public
		*/
		owner: null,

		/**
			@public
		*/
		autoConnect: true,

		/**
			@public
		*/
		autoSync: true,

		/**
			@public
		*/
		source: null,

		/**
			@public
		*/
		target: null,

		/**
			@public
		*/
		from: null,

		/**
			@public
		*/
		dirty: DIRTY_FROM,

		/**
			@public
		*/
		to: null,

		/**
			@public
		*/
		transform: null,

		/**
			@public
			@method
		*/
		isConnected: function () {
			return this.connected;
		},

		/**
			@public
			@method
		*/
		stop: function () {
			this._stop = true;
		},

		/**
			@public
			@method
		*/
		isReady: function () {
			return this.ready || ready(this);
		},

		/**
			@public
			@method
		*/
		reset: function () {
			this.disconnect();
			this.ready = null;
			this._source = this._target = this._to = this._from = this._toTarget = null;
			return this;
		},

		/**
			@public
			@method
		*/
		rebuild: function () {
			return this.reset().connect();
		},

		/**
			@public
			@method
		*/
		connect: function () {
			if (!this.isConnected()) {
				if (this.isReady()) {
					this._source.observe(this._from, this.onSource, this, {priority: true});

					// for two-way bindings we register to observe changes
					// from the target
					if (!this.oneWay) this._target.observe(this._to, this.onTarget, this);
					else if (this._toTarget) {
						this._target.observe(this._toTarget, this.onToTarget, this, {priority: true});
					}

					// we flag it as having been connected
					this.connected = true;
					if (this.autoSync) this.sync(true);
				}
			}

			return this;
		},

		/**
			@public
			@method
		*/
		disconnect: function () {
			if (this.isConnected()) {
				this._source.unobserve(this._from, this.onSource, this);

				// for two-way bindings we unregister the observer from
				// the target as well
				if (!this.oneWay) this._target.unobserve(this._to, this.onTarget, this);
				else if (this._toTarget) {
					this._target.unobserve(this._toTarget, this.onToTarget, this);
				}

				this.connected = false;
			}

			return this;
		},

		/**
			@public
			@method
		*/
		sync: function (force) {
			var source = this._source,
				target = this._target,
				from = this._from,
				to = this._to,
				xform = this.getTransform(),
				val;

			if (this.isReady() && this.isConnected()) {

				switch (this.dirty || (force && DIRTY_FROM)) {
				case DIRTY_TO:
					val = target.get(to);
					if (xform) val = xform.call(this.owner || this, val, DIRTY_TO, this);
					if (!this._stop) source.set(from, val, {create: false});
					break;
				case DIRTY_FROM:

				// @TODO: This should never need to happen but is here just in case
				// it is ever arbitrarily called not having been dirty?
				// default:
					val = source.get(from);
					if (xform) val = xform.call(this.owner || this, val, DIRTY_FROM, this);
					if (!this._stop) target.set(to, val, {create: false});
					break;
				}
				this.dirty = null;
				this._stop = null;
			}

			return this;
		},

		/**
			@private
		*/
		getTransform: function () {
			return this._didInitTransform ? this.transform : (function (bnd) {
				bnd._didInitTransform = true;

				var xform = bnd.transform,
					owner = bnd.owner,
					xformOwner = owner && owner.bindingTransformOwner;

				if (xform) {
					if (typeof xform == 'string') {
						if (xformOwner && xformOwner[xform]) {
							xform = xformOwner[xform];
						} else if (owner && owner[xform]) {
							xform = owner[xform];
						} else {
							xform = enyo.getPath(xform);
						}
					}

					/*jshint -W093 */
					return (bnd.transform = (typeof xform == 'function' ? xform : null));
					/*jshint +W093 */
				}
			})(this);
		},

		/**
			@private
		*/
		constructor: function (props) {
			bindings.push(this);

			if (props) enyo.mixin(this, props);

			if (!this.euid) this.euid = enyo.uid('b');
			if (this.autoConnect) this.connect();
		},

		/**
			@public
			@method
			@returns {this} Callee for chaining.
		*/
		destroy: function () {
			var owner = this.owner,
				idx;

			this.disconnect();
			this.owner = null;
			this.source = this._source = null;
			this.target = this._target = null;
			this.ready = null;
			this.destroyed = true;

			// @todo: remove me or postpone operation?
			idx = bindings.indexOf(this);
			if (idx > -1) bindings.splice(idx, 1);

			if (owner && !owner.destroyed) owner.removeBinding(this);

			return this;
		},

		/**
			@private
		*/
		onSource: function (was, is, path) {
			// @TODO: Should it...would it benefit from using these passed in values?
			this.dirty = this.dirty == DIRTY_TO ? null : DIRTY_FROM;
			return this.dirty == DIRTY_FROM && this.sync();
		},

		/**
			@private
		*/
		onTarget: function (was, is, path) {
			// @TODO: Same question as above, it seems useful but would it affect computed
			// properties or stale values?
			this.dirty = this.dirty == DIRTY_FROM ? null : DIRTY_TO;
			return this.dirty == DIRTY_TO && this.sync();
		},

		/**
			@private
		*/
		onToTarget: function (was, is, path) {
			this.dirty = DIRTY_FROM;
			this.reset().connect();
		}
	});

	/**
		@public
		@static
		@method
	*/
	enyo.Binding.find = function (euid) {
		return bindings.find(function (ln) {
			return ln.euid == euid;
		});
	};

	/**
		@public
		@static
	*/
	enyo.Binding.DIRTY_FROM = DIRTY_FROM;

	/**
		@public
		@static
	*/
	enyo.Binding.DIRTY_TO = DIRTY_TO;

	/**
		@public
		@static
	*/
	enyo.defaultBindingKind = enyo.Binding;

})(enyo, this);<commit_msg>onSource -> _sourceChanged; onTarget -> _targetChanged; onToTarget -> _toTargetChanged<commit_after>/**
	@namespace enyo
*/
(function (enyo, scope) {

	var kind = enyo.kind;

	/**
		All {@link enyo.Binding} instances are stored in this list and can be retrieved via the
		{@link enyo.Binding~find} method using a {@link enyo.Binding#id} to identify it.

		@public
		@name enyo.bindings
		@memberof enyo
	*/
	var bindings = enyo.bindings = [];

	var DIRTY_FROM = 0x01,
		DIRTY_TO = 0x02;

	/**
		Used to determine if a {@link enyo.Binding} is actually ready.

		@private
	*/
	function ready (binding) {
		var rdy = binding.ready;

		if (!rdy) {

			var from = binding.from || '',
				to = binding.to || '',
				source = binding.source,
				target = binding.target,
				owner = binding.owner,
				twoWay = !binding.oneWay,
				toTarget;

			if (typeof from != 'string') from = '';
			if (typeof to != 'string') to = '';

			if (!source) {

				// the worst case scenario here is for backward compatability purposes
				// we have to at least be able to derive the source via the from string
				if (from[0] == '^') {

					// this means we're reaching for a global
					from = from.slice(1);
					source = enyo.getPath.call(enyo.global, from);

				} else {
					source = owner;
				}

			}

			if (!target) {

				// same worst case as above, for backwards compatability purposes
				// we have to at least be able to derive the target via the to string
				if (to[0] == '^') {

					// this means we're reaching for a global
					to = to.slice(1);
					target = enyo.getPath.call(enyo.global, to);
				} else {
					target = owner;
				}
			}

			// we do this so we don't overwrite the originals in case we need to reset later
			binding._target = target;
			binding._source = source;
			binding._from = from[0] == '.'? from.slice(1): from;
			binding._to = to[0] == '.'? to.slice(1): to;

			if (!twoWay) {
				toTarget = binding._to.split('.');
				if (toTarget.length > 2) {
					toTarget.pop();
					binding._toTarget = toTarget.join('.');
				}
			}

			// now our sanitation
			rdy = !! (
				(source && (typeof source == 'object')) &&
				(target && (typeof source == 'object')) &&
				(from) &&
				(to)
			);
		}

		/*jshint -W093 */
		return (binding.ready = rdy);
		/*jshint +W093 */
	}

	/**
		@public
		@class enyo.Binding
	*/
	kind(
		/** @lends enyo.Binding.prototype */ {

		/**
			@private
		*/
		name: 'enyo.Binding',

		/**
			@private
		*/
		kind: null,

		/**
			@private
		*/
		noDefer: true,

		/**
			@public
		*/
		oneWay: true,

		/**
			@public
		*/
		connected: false,

		/**
			@public
		*/
		owner: null,

		/**
			@public
		*/
		autoConnect: true,

		/**
			@public
		*/
		autoSync: true,

		/**
			@public
		*/
		source: null,

		/**
			@public
		*/
		target: null,

		/**
			@public
		*/
		from: null,

		/**
			@public
		*/
		dirty: DIRTY_FROM,

		/**
			@public
		*/
		to: null,

		/**
			@public
		*/
		transform: null,

		/**
			@public
			@method
		*/
		isConnected: function () {
			return this.connected;
		},

		/**
			@public
			@method
		*/
		stop: function () {
			this._stop = true;
		},

		/**
			@public
			@method
		*/
		isReady: function () {
			return this.ready || ready(this);
		},

		/**
			@public
			@method
		*/
		reset: function () {
			this.disconnect();
			this.ready = null;
			this._source = this._target = this._to = this._from = this._toTarget = null;
			return this;
		},

		/**
			@public
			@method
		*/
		rebuild: function () {
			return this.reset().connect();
		},

		/**
			@public
			@method
		*/
		connect: function () {
			if (!this.isConnected()) {
				if (this.isReady()) {
					this._source.observe(this._from, this._sourceChanged, this, {priority: true});

					// for two-way bindings we register to observe changes
					// from the target
					if (!this.oneWay) this._target.observe(this._to, this._targetChanged, this);
					else if (this._toTarget) {
						this._target.observe(this._toTarget, this._toTargetChanged, this, {priority: true});
					}

					// we flag it as having been connected
					this.connected = true;
					if (this.autoSync) this.sync(true);
				}
			}

			return this;
		},

		/**
			@public
			@method
		*/
		disconnect: function () {
			if (this.isConnected()) {
				this._source.unobserve(this._from, this._sourceChanged, this);

				// for two-way bindings we unregister the observer from
				// the target as well
				if (!this.oneWay) this._target.unobserve(this._to, this._targetChanged, this);
				else if (this._toTarget) {
					this._target.unobserve(this._toTarget, this._toTargetChanged, this);
				}

				this.connected = false;
			}

			return this;
		},

		/**
			@public
			@method
		*/
		sync: function (force) {
			var source = this._source,
				target = this._target,
				from = this._from,
				to = this._to,
				xform = this.getTransform(),
				val;

			if (this.isReady() && this.isConnected()) {

				switch (this.dirty || (force && DIRTY_FROM)) {
				case DIRTY_TO:
					val = target.get(to);
					if (xform) val = xform.call(this.owner || this, val, DIRTY_TO, this);
					if (!this._stop) source.set(from, val, {create: false});
					break;
				case DIRTY_FROM:

				// @TODO: This should never need to happen but is here just in case
				// it is ever arbitrarily called not having been dirty?
				// default:
					val = source.get(from);
					if (xform) val = xform.call(this.owner || this, val, DIRTY_FROM, this);
					if (!this._stop) target.set(to, val, {create: false});
					break;
				}
				this.dirty = null;
				this._stop = null;
			}

			return this;
		},

		/**
			@private
		*/
		getTransform: function () {
			return this._didInitTransform ? this.transform : (function (bnd) {
				bnd._didInitTransform = true;

				var xform = bnd.transform,
					owner = bnd.owner,
					xformOwner = owner && owner.bindingTransformOwner;

				if (xform) {
					if (typeof xform == 'string') {
						if (xformOwner && xformOwner[xform]) {
							xform = xformOwner[xform];
						} else if (owner && owner[xform]) {
							xform = owner[xform];
						} else {
							xform = enyo.getPath(xform);
						}
					}

					/*jshint -W093 */
					return (bnd.transform = (typeof xform == 'function' ? xform : null));
					/*jshint +W093 */
				}
			})(this);
		},

		/**
			@private
		*/
		constructor: function (props) {
			bindings.push(this);

			if (props) enyo.mixin(this, props);

			if (!this.euid) this.euid = enyo.uid('b');
			if (this.autoConnect) this.connect();
		},

		/**
			@public
			@method
			@returns {this} Callee for chaining.
		*/
		destroy: function () {
			var owner = this.owner,
				idx;

			this.disconnect();
			this.owner = null;
			this.source = this._source = null;
			this.target = this._target = null;
			this.ready = null;
			this.destroyed = true;

			// @todo: remove me or postpone operation?
			idx = bindings.indexOf(this);
			if (idx > -1) bindings.splice(idx, 1);

			if (owner && !owner.destroyed) owner.removeBinding(this);

			return this;
		},

		/**
			@private
		*/
		_sourceChanged: function (was, is, path) {
			// @TODO: Should it...would it benefit from using these passed in values?
			this.dirty = this.dirty == DIRTY_TO ? null : DIRTY_FROM;
			return this.dirty == DIRTY_FROM && this.sync();
		},

		/**
			@private
		*/
		_targetChanged: function (was, is, path) {
			// @TODO: Same question as above, it seems useful but would it affect computed
			// properties or stale values?
			this.dirty = this.dirty == DIRTY_FROM ? null : DIRTY_TO;
			return this.dirty == DIRTY_TO && this.sync();
		},

		/**
			@private
		*/
		_toTargetChanged: function (was, is, path) {
			this.dirty = DIRTY_FROM;
			this.reset().connect();
		}
	});

	/**
		@public
		@static
		@method
	*/
	enyo.Binding.find = function (euid) {
		return bindings.find(function (ln) {
			return ln.euid == euid;
		});
	};

	/**
		@public
		@static
	*/
	enyo.Binding.DIRTY_FROM = DIRTY_FROM;

	/**
		@public
		@static
	*/
	enyo.Binding.DIRTY_TO = DIRTY_TO;

	/**
		@public
		@static
	*/
	enyo.defaultBindingKind = enyo.Binding;

})(enyo, this);<|endoftext|>


<commit_before>/*
 * The contents of this file are subject to the OpenMRS Public License
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and
 * limitations under the License.
 *
 * Copyright (C) OpenHMIS.  All Rights Reserved.
 */
curl(
	{ baseUrl: openhmis.url.resources },
	[
		openhmis.url.backboneBase + 'js/lib/jquery',
		openhmis.url.backboneBase + 'js/view/patient',
		openhmis.url.backboneBase + 'js/lib/i18n',
		openhmis.url.cashierBase + 'js/view/bill',
		openhmis.url.cashierBase + 'js/view/payment',
		openhmis.url.cashierBase + 'js/model/lineItem'
	],
	function($, openhmis, __) {
		var Screen = function() {
			this.billUuid = openhmis.getQueryStringParameter("billUuid");
			this.patientUuid = openhmis.getQueryStringParameter("patientUuid");

			this.patientView = new openhmis.PatientView();
			// Set up patient search selection handler
			openhmis.doSelectionHandler = this.patientView.takeRawPatient;

			var options = new openhmis.GenericModel([], {
				urlRoot: openhmis.url.page + openhmis.url.cashierBase + "options.json"
			});

			var self = this;
			options.fetch({ success: function(options, resp) {
				if (resp.exception) {
					openhmis.error({
						message: resp.exception.message,
						code: resp.exception.cause,
						detail: resp.exception.stackTrace
					});
				} else {
					self.createBillView.call(self, options, resp);
				}
			}});
		}

		/**
		 * createBillView
		 *
		 * Create the bill view, using specified options, then fetch data needed
		 * to populate the view.
		 *
		 * @param {Model} options A populated options model
		 */
		Screen.prototype.createBillView = function(options) {
			this.billView = new openhmis.BillView({
				roundToNearest: options.get("roundToNearest"),
				roundingMode: options.get("roundingMode")
			});
			var self = this;
			// If a bill is specified
			if (this.billUuid) {
				// Load the bill
				var bill = new openhmis.Bill({ uuid: this.billUuid });
				bill.fetch({
					silent: true,
					success: function(bill, resp) {
						self.setupBillViewWithBill.call(self, bill, resp);
					}
				});
			}
			// If a patient is specified
			else if (this.patientUuid) {
				var patient = new openhmis.Patient({ uuid: this.patientUuid });
				patient.fetch({
					silent: true,
					success: function(patient, resp) {
						self.setupBillViewWithPatient.call(self, patient, resp);
					}
				});
			} else {
				this.displayBillView.call(this);
			}
		};

		/*
		 * setupBillViewWithBill
		 *
		 * For setting up the BillView with an existing bill
		 *
		 * @param {Bill} bill An initialized openhmis.Bill model
		 */
		Screen.prototype.setupBillViewWithBill = function(bill) {
			this.billView.setBill(bill);
			this.patientView.model = new openhmis.Patient(bill.get("patient"));
			if (bill.get("billAdjusted")) {
				var self = this;
				bill.get("billAdjusted").fetch({
					success: function(billAdjusted, resp) {
						self.displayBillView();
					}
				});
			} else {
				this.displayBillView();
			}
			this.patientView.selectPatient(this.patientView.model, {silent:true});
		}

		/*
		 * setupBillViewWithPatient
		 *
		 * For setting up the BillView to be associated with a patient
		 *
		 * @param {Patient} patient An initialized openhmis.Patient model
		 */
		Screen.prototype.setupBillViewWithPatient = function(patient) {
			this.billView.bill.set("patient", patient);
			this.patientView.model = patient;
			this.displayBillView();
		}

		/*
		 * displayBillView
		 *
		 * Take care of putting together and rendering all the elements of the
		 * BillView.  Relies on initialized billView and patientView
		 */
		Screen.prototype.displayBillView = function() {
			var self = this;
			// Easy access to status enumeration
			var BillStatus = this.billView.bill.BillStatus;

			// Automatic receipt printing
			if (openhmis.getQueryStringParameter("print") === "true") {
				this.billView.printReceipt();
				$("#printReceipt").attr("disabled", "disabled");
			}

			// Patient View
			if (this.billView.bill.get("status") !== BillStatus.PENDING) {
				this.patientView.readOnly = true;
			}
			this.patientView.setElement($('#patient-view'));
			this.patientView.render();

			this.billView.on("save paid adjusted", function(bill) {
				window.location = openhmis.url.getPage("cashierBase") + 'bill.form?billUuid=' + bill.id;
			});
			this.billView.on("saveAndPrint", function(bill) {
				var url = openhmis.url.getPage("cashierBase") + 'bill.form?billUuid=' + bill.id;
				url = openhmis.addQueryStringParameter(url, "print=true");
				window.location = url;
			});

			this.billView.setElement($('#bill'));
			$saveButton = $('#saveBill');
			$postButton = $('#postBill');
			$printButton = $("#printReceipt");
			switch (this.billView.bill.get("status")) {
				case BillStatus.PENDING:
					$saveButton.val(__("Save Bill"));

					var inst = this;
					$saveButton.click(function() {
						inst.billView.saveBill();
					});

					var confirmMsg = __("Are you sure you want to post this bill?");
					$postButton.click(function() { if (confirm(confirmMsg)) { self.billView.postBill() }});
					$postButton.show();
					$printButton.val(__("Post & Print"));
					$printButton.click(function() { if (confirm(confirmMsg)) { self.billView.postBill({ print: true }) }});
					$printButton.show();

					if (this.billView.bill.get("billAdjusted")) {
						adjustedBillView = new openhmis.BillAndPaymentsView({
							model: this.billView.bill.get("billAdjusted")
						});
						$("#patient-view").after(adjustedBillView.el).addClass("combineBoxes");
						adjustedBillView.render();
						$("#bill").appendTo(adjustedBillView.$el);
						this.billView.options.listTitle = __("Adjustments");
					}

					// Provide cash point select, if this option is enabled
					var $cashPointLi = $("li.cashPoint");
					if (!$cashPointLi.hasClass("timesheet") && !this.billView.bill.get("billAdjusted")) {
						this.billView.setupCashPointForm($("li.cashPoint"));
					}
					break;
				case BillStatus.POSTED:
				case BillStatus.PAID:
					var $allowBillAdjustment = $('#allowBillAdjustment');
                    if ($allowBillAdjustment.val() == 'true'){
	                        $saveButton.val(__("Adjust Bill"));
	                        $saveButton.click(this.billView.handleAdjustBill);
					} else {
                        $saveButton.hide();
					}
					$printButton.val(__("Print Receipt"));
					 $printButton.click(function (event) {
						self.billView.printReceipt(event);
						$(this).attr("disabled", "disabled");
					});
					$printButton.show();
					break;
				case BillStatus.ADJUSTED:
					$saveButton.remove();
					break;
			}

			this.billView.render();

			if (this.billView.bill.get("status") === BillStatus.PENDING) {
				this.billView.setupNewItem();
			}

			this.patientView.on('selected', this.billView.patientSelected);
			this.patientView.on('editing', this.billView.blur);

			// Payment View
			var readOnly = !(this.billView.bill.get("status") == BillStatus.PENDING
							 || this.billView.bill.get("status") == BillStatus.POSTED);
			var paymentView = new openhmis.PaymentView({
				paymentCollection: this.billView.bill.get("payments"),
				processCallback: this.billView.processPayment,
				readOnly: readOnly
			});
			// Disable add event when the bill is saving to prevent
			// unsettling page drawing
			this.billView.on("save", function() { paymentView.model.off("add"); });
			paymentView.paymentCollection.on("remove", this.billView.updateTotals);
			paymentView.setElement($('#payment'));
			paymentView.render();

			this.billView.on("focusNext", paymentView.focus);

			window.onbeforeunload = function() {
				if (self.billView.bill.isUnsaved()) {
					return __("There are unsaved changes.");
				}
				return null;
			}

			if (this.billView.bill.get("patient")) {
				this.billView.focus();
			} else {
				$('#inputNode').focus();
			}
		}

		$(document).ready(function() {
			if ($("#bill").length > 0) {
				var screen = new Screen();
			}
		});

		return Screen;
	}
);
<commit_msg>	Changes indentation of the code 		^Cash-185<commit_after>/*
 * The contents of this file are subject to the OpenMRS Public License
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and
 * limitations under the License.
 *
 * Copyright (C) OpenHMIS.  All Rights Reserved.
 */
curl(
	{ baseUrl: openhmis.url.resources },
	[
		openhmis.url.backboneBase + 'js/lib/jquery',
		openhmis.url.backboneBase + 'js/view/patient',
		openhmis.url.backboneBase + 'js/lib/i18n',
		openhmis.url.cashierBase + 'js/view/bill',
		openhmis.url.cashierBase + 'js/view/payment',
		openhmis.url.cashierBase + 'js/model/lineItem'
	],
	function($, openhmis, __) {
		var Screen = function() {
			this.billUuid = openhmis.getQueryStringParameter("billUuid");
			this.patientUuid = openhmis.getQueryStringParameter("patientUuid");

			this.patientView = new openhmis.PatientView();
			// Set up patient search selection handler
			openhmis.doSelectionHandler = this.patientView.takeRawPatient;

			var options = new openhmis.GenericModel([], {
				urlRoot: openhmis.url.page + openhmis.url.cashierBase + "options.json"
			});

			var self = this;
			options.fetch({ success: function(options, resp) {
				if (resp.exception) {
					openhmis.error({
						message: resp.exception.message,
						code: resp.exception.cause,
						detail: resp.exception.stackTrace
					});
				} else {
					self.createBillView.call(self, options, resp);
				}
			}});
		}

		/**
		 * createBillView
		 *
		 * Create the bill view, using specified options, then fetch data needed
		 * to populate the view.
		 *
		 * @param {Model} options A populated options model
		 */
		Screen.prototype.createBillView = function(options) {
			this.billView = new openhmis.BillView({
				roundToNearest: options.get("roundToNearest"),
				roundingMode: options.get("roundingMode")
			});
			var self = this;
			// If a bill is specified
			if (this.billUuid) {
				// Load the bill
				var bill = new openhmis.Bill({ uuid: this.billUuid });
				bill.fetch({
					silent: true,
					success: function(bill, resp) {
						self.setupBillViewWithBill.call(self, bill, resp);
					}
				});
			}
			// If a patient is specified
			else if (this.patientUuid) {
				var patient = new openhmis.Patient({ uuid: this.patientUuid });
				patient.fetch({
					silent: true,
					success: function(patient, resp) {
						self.setupBillViewWithPatient.call(self, patient, resp);
					}
				});
			} else {
				this.displayBillView.call(this);
			}
		};

		/*
		 * setupBillViewWithBill
		 *
		 * For setting up the BillView with an existing bill
		 *
		 * @param {Bill} bill An initialized openhmis.Bill model
		 */
		Screen.prototype.setupBillViewWithBill = function(bill) {
			this.billView.setBill(bill);
			this.patientView.model = new openhmis.Patient(bill.get("patient"));
			if (bill.get("billAdjusted")) {
				var self = this;
				bill.get("billAdjusted").fetch({
					success: function(billAdjusted, resp) {
						self.displayBillView();
					}
				});
			} else {
				this.displayBillView();
			}
			this.patientView.selectPatient(this.patientView.model, {silent:true});
		}

		/*
		 * setupBillViewWithPatient
		 *
		 * For setting up the BillView to be associated with a patient
		 *
		 * @param {Patient} patient An initialized openhmis.Patient model
		 */
		Screen.prototype.setupBillViewWithPatient = function(patient) {
			this.billView.bill.set("patient", patient);
			this.patientView.model = patient;
			this.displayBillView();
		}

		/*
		 * displayBillView
		 *
		 * Take care of putting together and rendering all the elements of the
		 * BillView.  Relies on initialized billView and patientView
		 */
		Screen.prototype.displayBillView = function() {
			var self = this;
			// Easy access to status enumeration
			var BillStatus = this.billView.bill.BillStatus;

			// Automatic receipt printing
			if (openhmis.getQueryStringParameter("print") === "true") {
				this.billView.printReceipt();
				$("#printReceipt").attr("disabled", "disabled");
			}

			// Patient View
			if (this.billView.bill.get("status") !== BillStatus.PENDING) {
				this.patientView.readOnly = true;
			}
			this.patientView.setElement($('#patient-view'));
			this.patientView.render();

			this.billView.on("save paid adjusted", function(bill) {
				window.location = openhmis.url.getPage("cashierBase") + 'bill.form?billUuid=' + bill.id;
			});
			this.billView.on("saveAndPrint", function(bill) {
				var url = openhmis.url.getPage("cashierBase") + 'bill.form?billUuid=' + bill.id;
				url = openhmis.addQueryStringParameter(url, "print=true");
				window.location = url;
			});

			this.billView.setElement($('#bill'));
			$saveButton = $('#saveBill');
			$postButton = $('#postBill');
			$printButton = $("#printReceipt");
			switch (this.billView.bill.get("status")) {
				case BillStatus.PENDING:
					$saveButton.val(__("Save Bill"));

					var inst = this;
					$saveButton.click(function() {
						inst.billView.saveBill();
					});

					var confirmMsg = __("Are you sure you want to post this bill?");
					$postButton.click(function() { if (confirm(confirmMsg)) { self.billView.postBill() }});
					$postButton.show();
					$printButton.val(__("Post & Print"));
					$printButton.click(function() { if (confirm(confirmMsg)) { self.billView.postBill({ print: true }) }});
					$printButton.show();

					if (this.billView.bill.get("billAdjusted")) {
						adjustedBillView = new openhmis.BillAndPaymentsView({
							model: this.billView.bill.get("billAdjusted")
						});
						$("#patient-view").after(adjustedBillView.el).addClass("combineBoxes");
						adjustedBillView.render();
						$("#bill").appendTo(adjustedBillView.$el);
						this.billView.options.listTitle = __("Adjustments");
					}

					// Provide cash point select, if this option is enabled
					var $cashPointLi = $("li.cashPoint");
					if (!$cashPointLi.hasClass("timesheet") && !this.billView.bill.get("billAdjusted")) {
						this.billView.setupCashPointForm($("li.cashPoint"));
					}
					break;
				case BillStatus.POSTED:
				case BillStatus.PAID:
					var $allowBillAdjustment = $('#allowBillAdjustment');
					if ($allowBillAdjustment.val() == 'true'){
						$saveButton.val(__("Adjust Bill"));
						$saveButton.click(this.billView.handleAdjustBill);
					} else {
						$saveButton.hide();
					}
					$printButton.val(__("Print Receipt"));
					$printButton.click(function (event) {
						self.billView.printReceipt(event);
						$(this).attr("disabled", "disabled");
					});
					$printButton.show();
					break;
				case BillStatus.ADJUSTED:
					$saveButton.remove();
					break;
			}

			this.billView.render();

			if (this.billView.bill.get("status") === BillStatus.PENDING) {
				this.billView.setupNewItem();
			}

			this.patientView.on('selected', this.billView.patientSelected);
			this.patientView.on('editing', this.billView.blur);

			// Payment View
			var readOnly = !(this.billView.bill.get("status") == BillStatus.PENDING
							 || this.billView.bill.get("status") == BillStatus.POSTED);
			var paymentView = new openhmis.PaymentView({
				paymentCollection: this.billView.bill.get("payments"),
				processCallback: this.billView.processPayment,
				readOnly: readOnly
			});
			// Disable add event when the bill is saving to prevent
			// unsettling page drawing
			this.billView.on("save", function() { paymentView.model.off("add"); });
			paymentView.paymentCollection.on("remove", this.billView.updateTotals);
			paymentView.setElement($('#payment'));
			paymentView.render();

			this.billView.on("focusNext", paymentView.focus);

			window.onbeforeunload = function() {
				if (self.billView.bill.isUnsaved()) {
					return __("There are unsaved changes.");
				}
				return null;
			}

			if (this.billView.bill.get("patient")) {
				this.billView.focus();
			} else {
				$('#inputNode').focus();
			}
		}

		$(document).ready(function() {
			if ($("#bill").length > 0) {
				var screen = new Screen();
			}
		});

		return Screen;
	}
);
<|endoftext|>


<commit_before>export { Base } from "./Operation/Operation_Base.js";
export { Root } from "./Operation/Operation_Base.js";
export { TwinArray } from "./Operation/Operation_TwinArray.js";
export { AddTwoTensors } from "./Operation/Operation_AddTwoTensors.js";
export { MultiplyTwoTensors } from "./Operation/Operation_MultiplyTwoTensors.js";
export { ConcatShuffleSplit } from "./Operation/Operation_ConcatShuffleSplit.js";
export { ConcatAlongAxisId2 } from "./Operation/Operation_ConcatAlongAxisId2.js";
export { Depthwise } from "./Operation/Operation_Depthwise.js";
export { Depthwise_SameWhenPassThrough } from "./Operation/Operation_Depthwise_SameWhenPassThrough.js";
export { Depthwise_ConstantWhenPassThrough } from "./Operation/Operation_Depthwise_ConstantWhenPassThrough.js";
export { Pointwise } from "./Operation/Operation_Pointwise.js";
export { Pointwise_SameWhenPassThrough } from "./Operation/Operation_Pointwise_SameWhenPassThrough.js";
export { Pointwise_ConstantWhenPassThrough } from "./Operation/Operation_Pointwise_ConstantWhenPassThrough.js";
<commit_msg>Update Operation.js<commit_after>export { Base } from "./Operation/Operation_Base.js";
export { Root } from "./Operation/Operation_Base.js";
export { Dummy } from "./Operation/Operation_Dummy.js";
export { TwinArray } from "./Operation/Operation_TwinArray.js";
export { AddTwoTensors } from "./Operation/Operation_AddTwoTensors.js";
export { MultiplyTwoTensors } from "./Operation/Operation_MultiplyTwoTensors.js";
export { ConcatShuffleSplit } from "./Operation/Operation_ConcatShuffleSplit.js";
export { ConcatAlongAxisId2 } from "./Operation/Operation_ConcatAlongAxisId2.js";
export { Depthwise } from "./Operation/Operation_Depthwise.js";
export { Depthwise_SameWhenPassThrough } from "./Operation/Operation_Depthwise_SameWhenPassThrough.js";
export { Depthwise_ConstantWhenPassThrough } from "./Operation/Operation_Depthwise_ConstantWhenPassThrough.js";
export { Pointwise } from "./Operation/Operation_Pointwise.js";
export { Pointwise_SameWhenPassThrough } from "./Operation/Operation_Pointwise_SameWhenPassThrough.js";
export { Pointwise_ConstantWhenPassThrough } from "./Operation/Operation_Pointwise_ConstantWhenPassThrough.js";
<|endoftext|>