<commit_before>jest.mock('../../../../managers/meeting');
jest.mock('../../../../models/meeting.accessors');
jest.mock('../../../../models/user.accessors');
const { adminSetPermissions, toggleCanVote } = require('../toggle_vote');
const { canEdit } = require('../../../../managers/meeting');
const { getActiveGenfors, getGenfors } = require('../../../../models/meeting.accessors');
const { getUserById, updateUserById } = require('../../../../models/user.accessors');
const { generateGenfors, generateManager, generateUser, generateSocket } = require('../../../../utils/generateTestData');
const permissions = require('../../../../../common/auth/permissions');

describe('toggleCanVote', () => {
  beforeEach(() => {
    updateUserById.mockImplementation(
      async (userid, data) => generateUser({ id: userid, ...data }));
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.CAN_VOTE,
    }));
  });

  const generateData = () => ({
    id: 1,
    canVote: false,
  });

  it('emits toggled can vote action on success', async () => {
    const socket = generateSocket();
    await toggleCanVote(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toMatchSnapshot();
  });

  it('emits error when user does not have CAN_VOTE permission', async () => {
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.IS_LOGGED_IN,
    }));
    const socket = generateSocket();

    await toggleCanVote(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toMatchSnapshot();
  });
});

describe('setUserPermissions', () => {
  beforeEach(() => {
    const genfors = generateGenfors();
    updateUserById.mockImplementation(
      async (userid, data) => generateUser({ id: userid, ...data }));
    getActiveGenfors.mockImplementation(() => genfors);
    getGenfors.mockImplementation(() => genfors);
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.IS_LOGGED_IN,
    }));
    canEdit.mockImplementation((required, user) => user.permissions >= required);
  });

  const generateData = () => ({
    id: 1,
    canVote: true,
    permissions: permissions.CAN_VOTE,
  });

  it('updates user permissions if requested and user authorized', async () => {
    const socket = generateSocket(generateManager());

    await adminSetPermissions(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.to('admin').emit.mock.calls).toMatchSnapshot();
  });

  it('does not update user permissions if not authorized', async () => {
    const socket = generateSocket(generateUser());

    await adminSetPermissions(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });

  it('emits error when failing to update user permissions', async () => {
    updateUserById.mockImplementation(() => { throw new Error(); });
    const socket = generateSocket(generateManager());

    await adminSetPermissions(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });
});
<commit_msg>Add listener tests for toggle_vote<commit_after>jest.mock('../../../../managers/meeting');
jest.mock('../../../../models/meeting.accessors');
jest.mock('../../../../models/user.accessors');
const { listener, adminSetPermissions, toggleCanVote } = require('../toggle_vote');
const { canEdit } = require('../../../../managers/meeting');
const { getActiveGenfors, getGenfors } = require('../../../../models/meeting.accessors');
const { getUserById, updateUserById } = require('../../../../models/user.accessors');
const { generateGenfors, generateManager, generateUser, generateSocket } = require('../../../../utils/generateTestData');
const permissions = require('../../../../../common/auth/permissions');
const { ADMIN_SET_PERMISSIONS, ADMIN_TOGGLE_CAN_VOTE } = require('../../../../../common/actionTypes/users');

describe('toggleCanVote', () => {
  beforeEach(() => {
    updateUserById.mockImplementation(
      async (userid, data) => generateUser({ id: userid, ...data }));
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.CAN_VOTE,
    }));
  });

  const generateData = () => ({
    id: 1,
    canVote: false,
  });

  it('emits toggled can vote action on success', async () => {
    const socket = generateSocket();
    await toggleCanVote(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toMatchSnapshot();
  });

  it('emits error when user does not have CAN_VOTE permission', async () => {
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.IS_LOGGED_IN,
    }));
    const socket = generateSocket();

    await toggleCanVote(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toMatchSnapshot();
  });
});

describe('setUserPermissions', () => {
  beforeEach(() => {
    const genfors = generateGenfors();
    updateUserById.mockImplementation(
      async (userid, data) => generateUser({ id: userid, ...data }));
    getActiveGenfors.mockImplementation(() => genfors);
    getGenfors.mockImplementation(() => genfors);
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.IS_LOGGED_IN,
    }));
    canEdit.mockImplementation((required, user) => user.permissions >= required);
  });

  const generateData = () => ({
    id: 1,
    canVote: true,
    permissions: permissions.CAN_VOTE,
  });

  it('updates user permissions if requested and user authorized', async () => {
    const socket = generateSocket(generateManager());

    await adminSetPermissions(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.to('admin').emit.mock.calls).toMatchSnapshot();
  });

  it('does not update user permissions if not authorized', async () => {
    const socket = generateSocket(generateUser());

    await adminSetPermissions(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });

  it('emits error when failing to update user permissions', async () => {
    updateUserById.mockImplementation(() => { throw new Error(); });
    const socket = generateSocket(generateManager());

    await adminSetPermissions(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });
});

function generateSocketData(data) {
  return { type: '', ...data };
}

describe('listener', () => {
  beforeEach(() => {
    // Why tho
    canEdit.mockImplementation((required, user) => user.permissions >= required);
    getActiveGenfors.mockImplementation(async () => generateGenfors());
    getGenfors.mockImplementation(async () => generateGenfors());
    updateUserById.mockImplementation(
      async (userid, data) => generateUser({ id: userid, ...data }));
    getUserById.mockImplementation(async userid => generateUser({
      id: userid,
      permissions: permissions.CAN_VOTE,
    }));
  });

  it('listens to ADMIN_SET_PERMISSIONS', async (done) => {
    const socket = generateSocket(generateManager());
    await listener(socket);

    await socket.mockEmit('action', generateSocketData({ type: ADMIN_SET_PERMISSIONS }));

    setTimeout(() => {
      expect(socket.emit).toBeCalled();
      expect(socket.broadcast.emit).toBeCalled();
      done();
    });
  });

  it('listens to ADMIN_TOGGLE_CAN_VOTE', async (done) => {
    const socket = generateSocket(generateManager());
    await listener(socket);

    await socket.mockEmit('action', generateSocketData({ type: ADMIN_TOGGLE_CAN_VOTE }));

    setTimeout(() => {
      expect(socket.emit).toBeCalled();
      expect(socket.broadcast.emit).toBeCalled();
      done();
    });
  });

  it('ignores INVALID_ACTION', async () => {
    const socket = generateSocket();
    await listener(socket);

    await socket.mockEmit('action', generateSocketData({ type: 'INVALID_ACTION' }));

    expect(socket.emit).not.toBeCalled();
    expect(socket.broadcast.emit).not.toBeCalled();
  });
});
<|endoftext|>


<commit_before>'use strict';

/**
 * This pipeline stage loads song info from external services.
 */

define((require) => {
	const Options = require('storage/options');
	const ScrobbleService = require('object/scrobble-service');

	const INFO_TO_COPY = [
		'duration', 'artist', 'track',
	];
	const METADATA_TO_COPY = [
		'trackArtUrl', 'artistUrl', 'trackUrl',
		'albumUrl', 'userPlayCount', 'albumMbId',
	];

	/**
	 * Load song info using ScrobblerService object.
	 * @param  {Object} song Song instance
	 */
	async function process(song) {
		if (song.isEmpty()) {
			return;
		}

		const songInfoArr = await ScrobbleService.getSongInfo(song);

		for (const field of METADATA_TO_COPY) {
			delete song.metadata[field];
		}

		const songInfo = getInfo(songInfoArr);
		const isSongValid = songInfo !== null;
		if (isSongValid) {
			if (!song.flags.isCorrectedByUser) {
				for (const field of INFO_TO_COPY) {
					song.processed[field] = songInfo[field];
				}

				if (!song.getAlbum()) {
					song.processed.album = songInfo.album;
				}
			}

			for (const field of METADATA_TO_COPY) {
				song.metadata[field] = songInfo[field];
			}
		}

		const forceRecognize = await Options.getOption(Options.FORCE_RECOGNIZE);
		song.flags.isValid = isSongValid || forceRecognize;
	}

	/**
	 * Get song info from array contains the highest keys count.
	 * @param  {Array} songInfoArr Array of song info objects
	 * @return {Object} Song info object
	 */
	function getInfo(songInfoArr) {
		return songInfoArr.reduce((prev, current) => {
			if (!current) {
				return prev;
			}
			if (!prev) {
				return current;
			}
			if (getNonEmptyKeyCount(current) > getNonEmptyKeyCount(prev)) {
				return current;
			}

			return prev;
		}, null);
	}

	/**
	 * Return number of non-empty object keys.
	 * @param  {Object} obj Object instance
	 * @return {Number} Number of non-empty object keys
	 */
	function getNonEmptyKeyCount(obj) {
		let keyCount = 0;
		for (const key in obj) {
			if (obj[key]) {
				++keyCount;
			}
		}

		return keyCount;
	}

	return { process };
});
<commit_msg>Remove redundant code block<commit_after>'use strict';

/**
 * This pipeline stage loads song info from external services.
 */

define((require) => {
	const Options = require('storage/options');
	const ScrobbleService = require('object/scrobble-service');

	const INFO_TO_COPY = [
		'duration', 'artist', 'track',
	];
	const METADATA_TO_COPY = [
		'trackArtUrl', 'artistUrl', 'trackUrl',
		'albumUrl', 'userPlayCount', 'albumMbId',
	];

	/**
	 * Load song info using ScrobblerService object.
	 * @param  {Object} song Song instance
	 */
	async function process(song) {
		if (song.isEmpty()) {
			return;
		}

		const songInfoArr = await ScrobbleService.getSongInfo(song);
		const songInfo = getInfo(songInfoArr);
		const isSongValid = songInfo !== null;
		if (isSongValid) {
			if (!song.flags.isCorrectedByUser) {
				for (const field of INFO_TO_COPY) {
					song.processed[field] = songInfo[field];
				}

				if (!song.getAlbum()) {
					song.processed.album = songInfo.album;
				}
			}

			for (const field of METADATA_TO_COPY) {
				song.metadata[field] = songInfo[field];
			}
		}

		const forceRecognize = await Options.getOption(Options.FORCE_RECOGNIZE);
		song.flags.isValid = isSongValid || forceRecognize;
	}

	/**
	 * Get song info from array contains the highest keys count.
	 * @param  {Array} songInfoArr Array of song info objects
	 * @return {Object} Song info object
	 */
	function getInfo(songInfoArr) {
		return songInfoArr.reduce((prev, current) => {
			if (!current) {
				return prev;
			}
			if (!prev) {
				return current;
			}
			if (getNonEmptyKeyCount(current) > getNonEmptyKeyCount(prev)) {
				return current;
			}

			return prev;
		}, null);
	}

	/**
	 * Return number of non-empty object keys.
	 * @param  {Object} obj Object instance
	 * @return {Number} Number of non-empty object keys
	 */
	function getNonEmptyKeyCount(obj) {
		let keyCount = 0;
		for (const key in obj) {
			if (obj[key]) {
				++keyCount;
			}
		}

		return keyCount;
	}

	return { process };
});
<|endoftext|>


<commit_before>var TAG = "DEMO",
    DFP = require("ti.dfp"),
    _mode = true;

// List ads to be created
var banners = {
    default: null,
    smart: null,
    sizes: null
};

// Construct
(function(args) {
    Ti.API.info(TAG, "Construct ads page");
    $.version.text += Ti.App.version;
    resetButton();

    // Instantiate DFP views
    banners.default = DFP.createView({
        top: 20,
        width: 320,
        height: 50,
        adWidth: 320,
        adHeight: 50,
        adUnitId: "/6236286/defaultBanner",
        location: {
            latitude: 52.3702157,
            longitude: 4.89516789,
            accuracy: 10
        }
    });

    banners.smart = DFP.createView({
        debug: true,
        top: 20,
        width: Ti.UI.FILL,
        height: 50,
        adHeight: 50,
        testDevices: [
            // "8DC9DB6D6C9B1C0C7D29BF69155FC0AB",
            // "testDeviceID"
        ]
    });

    banners.sizes = DFP.createView({
        top: 20,
        width: Ti.UI.FILL,
        height: 250,
        adUnitId: "/6236286/sizesBanner",
        adSizes: [
            {width: 320, height: 50},
            {width: 320, height: 100},
            {width: 300, height: 100},
            {width: 300, height: 250}
        ]
    });

    // Add event listeners
    banners.default.addEventListener("receivead", doReceiveAd);
    banners.smart.addEventListener("receivead", doReceiveAd);
    banners.sizes.addEventListener("receivead", doReceiveAd);

    // Add ads to view
    $.defaultAd.add(banners.default);
    $.smartAd.add(banners.smart);
    $.sizesAd.add(banners.sizes);
})(arguments[0]);

//  Handle button events
function doButtonClick() {
    // Simulate later unit ID change
    banners.smart.adUnitId = "/6236286/DFP-mode";
    banners.smart.extras = {
        mode: _mode?1:2
    };
    _mode = !_mode; // switch mode

    $.refreshBtn.setTitle("Requesting...");
    $.refreshBtn.setEnabled(false);
    requestAd();
}
function resetButton() {
    $.refreshBtn.setTitle("Manual refresh");
    $.refreshBtn.setEnabled(true);
}

// Handle ad events
function requestAd() {
    _.each(banners, function(banner, name) {
        if(banner) {
            Ti.API.info(TAG, "requesting ad for " + name);
            banner.requestAd();
        }
    });
}
function doReceiveAd(evt) {
    resetButton();
    // If it failed, event object contains an attribute 'error'
    if(evt && evt.error) {
        return Ti.API.error(TAG, "error receiving ad", evt);
    }
    Ti.API.info(TAG, "received ad", evt.width, evt.height);
}

/*** ABOUT SIZING on iOS
 ** DYNAMIC: vertical/horizontal layout
 * use evt.source.setHeight(evt.height)
 * Example in penton.js
 *
 ** STATIC
 * specify view size (height: 50) to match adSize (adWidth, ...)
 * Warning: Ti.UI.SIZE doesn't work (spent 2 days on it)
 */
<commit_msg>Clean & improve demo<commit_after>var TAG = "DEMO",
    DFP = require("ti.dfp"),
    _mode = true;

// List ads to be created
var banners = {
    default: null,
    smart: null,
    sizes: null
};

// Construct
(function(args) {
    Ti.API.info(TAG, "Construct ads page");
    $.version.text += Ti.App.version;
    resetButton();

    // Instantiate DFP views
    banners.default = DFP.createView({
        top: 20,
        width: 320,
        height: 50,
        adWidth: 320,
        adHeight: 50,
        adUnitId: "/6236286/defaultBanner",
        location: {
            latitude: 52.3702157,
            longitude: 4.89516789,
            accuracy: 10
        }
    });

    banners.smart = DFP.createView({
        debug: true,
        top: 20,
        width: Ti.UI.FILL,
        height: 50
    });

    banners.sizes = DFP.createView({
        top: 20,
        width: Ti.UI.FILL,
        height: 250,
        adUnitId: "/6236286/sizesBanner",
        adSizes: [
            {width: 320, height: 50},
            {width: 320, height: 100},
            {width: 300, height: 100},
            {width: 300, height: 250}
        ],
        testDevices: [
            "8DC9DB6D6C9B1C0C7D29BF69155FC0AB",
            "simulator"
        ]
    });

    // Add event listeners
    banners.default.addEventListener("receivead", doReceiveAd);
    banners.smart.addEventListener("receivead", doReceiveAd);
    banners.sizes.addEventListener("receivead", doReceiveAd);

    // Add ads to view
    $.defaultAd.add(banners.default);
    $.smartAd.add(banners.smart);
    $.sizesAd.add(banners.sizes);
})(arguments[0]);

//  Handle button events
function doButtonClick() {
    // Simulate later unit ID change
    banners.smart.adUnitId = "/6236286/DFP-mode";
    banners.smart.extras = {
        mode: _mode?1:2
    };
    _mode = !_mode; // switch mode

    $.refreshBtn.setTitle("Requesting...");
    $.refreshBtn.setEnabled(false);
    requestAd();
}
function resetButton() {
    $.refreshBtn.setTitle("Manual refresh");
    $.refreshBtn.setEnabled(true);
}

// Handle ad events
function requestAd() {
    _.each(banners, function(banner, name) {
        if(banner) {
            Ti.API.info(TAG, "requesting ad for " + name);
            banner.requestAd();
        }
    });
}
function doReceiveAd(evt) {
    resetButton();
    // If it failed, event object contains an attribute 'error'
    if(evt && evt.error) {
        return Ti.API.error(TAG, "error receiving ad", evt);
    }
    Ti.API.info(TAG, "received ad", evt.width, evt.height);
    if(OS_ANDROID) return;
    evt.source.setWidth(evt.width);
    evt.source.setHeight(evt.height);
}

/*** ABOUT SIZING on iOS
 ** DYNAMIC: vertical/horizontal layout
 * use evt.source.setHeight(evt.height)
 * Example in penton.js
 *
 ** STATIC
 * specify view size (height: 50) to match adSize (adWidth, ...)
 * Warning: Ti.UI.SIZE doesn't work (spent 2 days on it)
 */
<|endoftext|>


<commit_before>(function () {

    "use strict";

    /**
     Quick-and-dirty utility for debugging XEO {{#crossLink "Scene"}}Scenes{{/crossLink}}.

     The utility has fluent builder API that lets you create and update simple objects to help you
     debug or demo things on XEO.

     The utility does not clean up any components it created with your {{#crossLink "Scene"}}Scenes{{/crossLink}}.

     ## Example

     Drawing a red triangle:

     ```` javascript
     XEO.debug
     .id("myTriangle")  // Supply an ID when you want to update the line if already existing
     .color([1,0,0])
     .pos([-10, -10, 0])
     .pos([10, -10, 0])
     .pos([0, -4, 0])
     .line();

     ````

     Updating the triangle to change it's color and position:

     ```` javascript
     XEO.debug
     .id("myTriangle")
     .pos([-15, -15, 0])
     .pos([15, -15, 0])
     .pos([0, 5, 0])
     .color([0,1,0])
     .line();

     ````

     Creating a label at a Given World position:

     ```` javascript
     XEO.debug
     .pos([-10, 34, 2])
     .text("Check this out!")
     .label();

     ````
     @class XEO.debug
     @static
     @author xeolabs / http://xeolabs.com/
     */
    XEO.debug = new (function () {

        // Converts XEO color to CSS
        function cssColor(color) {
            return "rgb(" +
                Math.floor(color[0] * 255) + "," +
                Math.floor(color[1] * 255) + "," +
                Math.floor(color[2] * 255) + ")";
        }

        var scene;
        var id;
        var color;
        var fillColor;
        var opacity;
        var lineWidth;
        var diffuse;
        var pos;
        var posi;
        var text;
        var radius;

        this.scene = function (_scene) {
            scene = _scene;
            return this;
        };

        function getScene() {
            return scene || XEO.scene;
        }

        this.id = function (value) {
            id = value;
            return this;
        };

        this.color = function (value) {
            color = value;
            return this;
        };

        this.fillColor = function (value) {
            fillColor = value;
            return this;
        };

        this.opacity = function (value) {
            opacity = value;
            return this;
        };

        this.lineWidth = function (value) {
            lineWidth = value;
            return this;
        };

        this.pos = function (value) {
            pos[posi++] = value;
            return this;
        };

        this.text = function (value) {
            text = value;
            return this;
        };

        this.radius = function (value) {
            radius = value;
            return this;
        };

        this._reset = function () {
            scene = undefined;
            color = [1, 0.4, 0.4];
            fillColor = [0, 0, 0];
            diffuse = [1, 0.4, 0.4];
            opacity = 1.0;
            lineWidth = 2.0;
            id = undefined;
            pos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
            posi = 0;
            text = "";
            radius = 1.0;
        };

        this.line = function () {

            if (posi < 2) {
                console.error("[XEO.debug.line] Not enough calls to XEO.debug.pos() for XEO.debug.line() - need at least two.");
                return;
            }

            var _id = id || "__debugLine";

            var positions = [];
            var indices = [];

            for (var i = 0; i < posi; i++) {
                positions.push(pos[i][0]);
                positions.push(pos[i][1]);
                positions.push(pos[i][2]);

                indices.push(i);
                if (i < (posi - 1)) {
                    indices.push(i + 1);
                }
            }

            var object = getScene().objects[_id];

            if (!object) {
                new XEO.GameObject(getScene(), {
                    id: _id,
                    geometry: new XEO.Geometry({
                        primitive: "lines",
                        positions: positions,
                        indices: indices
                    }),
                    material: new XEO.PhongMaterial({
                        emissive: color,
                        opacity: opacity,
                        lineWidth: lineWidth
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: opacity < 1.0
                    })
                });
            } else {
                object.geometry.positions = positions;
                object.geometry.indices = indices;
                object.material.emissive = color;
                object.material.opacity = opacity;
                object.modes.transparency = opacity < 1.0;
                object.material.lineWidth = lineWidth;
            }

            this._reset();

            return this;
        };

        this.label = function () {

            var _id = id || "__debugLabel";

            var object = getScene().objects[_id];

            if (!object) {

                object = new XEO.GameObject(getScene(), {
                    id: _id,
                    transform: new XEO.Translate({
                        xyz: pos[0]
                    }),
                    geometry: new XEO.SphereGeometry({
                        radius: 0.1
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: true
                    }),
                    material: new XEO.PhongMaterial({ // Hides the sphere while still rendering it
                        opacity: 0
                    })
                });

                var body = document.getElementsByTagName("body")[0];

                var boxDiv = document.createElement('div');
                boxDiv.innerText = text;
                var style = boxDiv.style;
                style.color = cssColor(color);
                style.position = "absolute";
                style.padding = "10px";
                style.margin = "0";
                style.background = fillColor ? cssColor(fillColor) : "black";
                style.opacity = opacity;
                style.border = lineWidth + "px solid " + cssColor(color);
                style["z-index"] = "1000";
                style.width = "auto";
                style.height = "auto";
                style["border-radius"] = "5px";
                style.font = "bold 12px arial,serif";
                body.appendChild(boxDiv);

                var pointDiv = document.createElement('div');
                style = pointDiv.style;
                style.color = "white";
                style.background = "white";
                style.position = "absolute";
                style.padding = "3px";
                style.margin = "0";
                style.background = "white";
                style.opacity = 1.0;
                style.border = lineWidth + "px solid " + cssColor(color);
                style.width = "2px";
                style.height = "2px";
                style["border-radius"] = "5px";
                style["z-index"] = "1001";
                body.appendChild(pointDiv);

                object.canvasBoundary.on("updated",
                    function () {

                        var center = object.canvasBoundary.center;

                        boxDiv.style.left = center[0] - 3 + "px";
                        boxDiv.style.top = center[1] - 3 + "px";

                        pointDiv.style.left = center[0] - 5 + "px";
                        pointDiv.style.top = center[1] - 5 + "px";

                        var zIndex = 10000 + Math.floor(object.viewBoundary.center[2]);

                        boxDiv.style["z-index"] = zIndex;
                        pointDiv.style["z-index"] = zIndex + 1;
                    });

                object.boxDiv = boxDiv; // Dirty, but all is encapsulated by the debug utility
                object.pointDiv = boxDiv;

            } else {
                object.transform.xyz = pos[0];

                object.boxDiv.innerText = text;
                object.boxDiv.style.opacity = opacity;
                object.boxDiv.style.background = cssColor(fillColor);
                object.boxDiv.style.color = cssColor(color);

                var border = lineWidth + "px solid " + cssColor(color);
                object.boxDiv.style.border = border;
                object.pointDiv.style.border = border;
            }

            this._reset();

            return this;
        };

        this.sphere = function () {

            var _id = id || "__debugSphere";

            var object = getScene().objects[_id];

            if (!object) {

                object = new XEO.GameObject(getScene(), {
                    id: _id,
                    transform: new XEO.Translate({
                        xyz: pos[0]
                    }),
                    geometry: new XEO.SphereGeometry({
                        radius: radius
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: opacity < 1.0,
                        backfaces: false
                    }),
                    material: new XEO.PhongMaterial({ // Hides the sphere while still rendering it
                        diffuse: color,
                        emissive: color,
                        opacity: opacity,
                        specular: [1,1,1]
                    })
                });

            } else {
                object.transform.xyz = pos[0];
                object.material.color = color;
                object.material.emissive = color;
                object.material.opacity = opacity;
                object.modes.transparency = opacity < 1.0;
            }

            this._reset();

            return this;
        };

        //---------------------------
        // Work in progress
        //---------------------------
        this.label3d = function () {

            var _id = id || "__debugLabel3d";

            var object = getScene().objects[_id];

            if (!object) {

                object = new XEO.GameObject(getScene(), {
                    id: _id,
                    transform: new XEO.Translate({
                        xyz: pos[0]
                    }),
                    geometry: new XEO.VectorTextGeometry({
                        text: text
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: true
                    }),
                    material: new XEO.PhongMaterial({ // Hides the sphere while still rendering it
                        emissive: color,
                        opacity: opacity,
                        lineWidth: lineWidth
                    }),
                    billboard: new XEO.Billboard({
                        spherical: true
                    })
                });

            } else {
                object.transform.xyz = pos[0];
                object.geometry.text = text;
                object.material.emissive = color;
                object.material.opacity = opacity;
                object.modes.transparency = opacity < 1.0;
                object.material.lineWidth = lineWidth;
            }

            this._reset();

            return this;
        };

        this._reset();
    })();

})();
<commit_msg>XEO.debug enhancements<commit_after>(function () {

    "use strict";

    /**
     Quick-and-dirty utility for debugging XEO {{#crossLink "Scene"}}Scenes{{/crossLink}}.

     The utility has fluent builder API that lets you create and update simple objects to help you
     debug or demo things on XEO.

     The utility does not clean up any components it created with your {{#crossLink "Scene"}}Scenes{{/crossLink}}.

     ## Example

     Drawing a red triangle:

     ```` javascript
     XEO.debug
     .id("myTriangle")  // Supply an ID when you want to update the line if already existing
     .color([1,0,0])
     .pos([-10, -10, 0])
     .pos([10, -10, 0])
     .pos([0, -4, 0])
     .line();

     ````

     Updating the triangle to change it's color and position:

     ```` javascript
     XEO.debug
     .id("myTriangle")
     .pos([-15, -15, 0])
     .pos([15, -15, 0])
     .pos([0, 5, 0])
     .color([0,1,0])
     .line();

     ````

     Creating a label at a Given World position:

     ```` javascript
     XEO.debug
     .pos([-10, 34, 2])
     .text("Check this out!")
     .label();

     ````
     @class XEO.debug
     @static
     @author xeolabs / http://xeolabs.com/
     */
    XEO.debug = new (function () {

        // Converts XEO color to CSS
        function cssColor(color) {
            return "rgb(" +
                Math.floor(color[0] * 255) + "," +
                Math.floor(color[1] * 255) + "," +
                Math.floor(color[2] * 255) + ")";
        }

        var scene;
        var id;
        var color;
        var fillColor;
        var opacity;
        var lineWidth;
        var diffuse;
        var pos;
        var posi;
        var text;
        var radius;

        this.scene = function (_scene) {
            scene = _scene;
            return this;
        };

        function getScene() {
            return scene || XEO.scene;
        }

        this.id = function (value) {
            id = value;
            return this;
        };

        this.color = function (value) {
            color = value;
            return this;
        };

        this.fillColor = function (value) {
            fillColor = value;
            return this;
        };

        this.opacity = function (value) {
            opacity = value;
            return this;
        };

        this.lineWidth = function (value) {
            lineWidth = value;
            return this;
        };

        this.pos = function (value) {
            pos[posi++] = value;
            return this;
        };

        this.text = function (value) {
            text = value;
            return this;
        };

        this.radius = function (value) {
            radius = value;
            return this;
        };

        this.show = function (id) {
            var object = getScene().objects[id];
            if (object) {
                object.visibility.visible = true;
            }
            return this;
        };

        this.hide = function (id) {
            var object = getScene().objects[id];
            if (object) {
                object.visibility.visible = false;
            }
            return this;
        };

        this._reset = function () {
            scene = undefined;
            color = [1, 0.4, 0.4];
            fillColor = [0, 0, 0];
            diffuse = [1, 0.4, 0.4];
            opacity = 1.0;
            lineWidth = 2.0;
            id = undefined;
            pos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
            posi = 0;
            text = "";
            radius = 1.0;
        };

        this.line = function () {

            if (posi < 2) {
                console.error("[XEO.debug.line] Not enough calls to XEO.debug.pos() for XEO.debug.line() - need at least two.");
                return;
            }

            var _id = id || "__debugLine";

            var positions = [];
            var indices = [];

            for (var i = 0; i < posi; i++) {
                positions.push(pos[i][0]);
                positions.push(pos[i][1]);
                positions.push(pos[i][2]);

                indices.push(i);
                if (i < (posi - 1)) {
                    indices.push(i + 1);
                }
            }

            var object = getScene().objects[_id];

            if (!object) {

                new XEO.GameObject(getScene(), {
                    id: _id,
                    geometry: new XEO.Geometry({
                        primitive: "lines",
                        positions: positions,
                        indices: indices
                    }),
                    material: new XEO.PhongMaterial({
                        emissive: color,
                        opacity: opacity,
                        lineWidth: lineWidth
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: opacity < 1.0
                    }),
                    visibility: new XEO.Visibility({  // This GameObject should not be pickable
                        visible: true
                    })
                });
            } else {
                object.geometry.positions = positions;
                object.geometry.indices = indices;
                object.material.emissive = color;
                object.material.opacity = opacity;
                object.modes.transparency = opacity < 1.0;
                object.material.lineWidth = lineWidth;
                object.visibility.visible = true;
            }

            this._reset();

            return this;
        };

        this.label = function () {

            var _id = id || "__debugLabel";

            var object = getScene().objects[_id];

            if (!object) {

                object = new XEO.GameObject(getScene(), {
                    id: _id,
                    transform: new XEO.Translate({
                        xyz: pos[0]
                    }),
                    geometry: new XEO.SphereGeometry({
                        radius: 0.1
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: true
                    }),
                    material: new XEO.PhongMaterial({ // Hides the sphere while still rendering it
                        opacity: 0
                    })
                });

                var body = document.getElementsByTagName("body")[0];

                var boxDiv = document.createElement('div');
                boxDiv.innerText = text;
                var style = boxDiv.style;
                style.color = cssColor(color);
                style.position = "absolute";
                style.padding = "10px";
                style.margin = "0";
                style.background = fillColor ? cssColor(fillColor) : "black";
                style.opacity = opacity;
                style.border = lineWidth + "px solid " + cssColor(color);
                style["z-index"] = "1000";
                style.width = "auto";
                style.height = "auto";
                style["border-radius"] = "5px";
                style.font = "bold 12px arial,serif";
                body.appendChild(boxDiv);

                var pointDiv = document.createElement('div');
                style = pointDiv.style;
                style.color = "white";
                style.background = "white";
                style.position = "absolute";
                style.padding = "3px";
                style.margin = "0";
                style.background = "white";
                style.opacity = 1.0;
                style.border = lineWidth + "px solid " + cssColor(color);
                style.width = "2px";
                style.height = "2px";
                style["border-radius"] = "5px";
                style["z-index"] = "1001";
                body.appendChild(pointDiv);

                object.canvasBoundary.on("updated",
                    function () {

                        var center = object.canvasBoundary.center;

                        boxDiv.style.left = center[0] - 3 + "px";
                        boxDiv.style.top = center[1] - 3 + "px";

                        pointDiv.style.left = center[0] - 5 + "px";
                        pointDiv.style.top = center[1] - 5 + "px";

                        var zIndex = 10000 + Math.floor(object.viewBoundary.center[2]);

                        boxDiv.style["z-index"] = zIndex;
                        pointDiv.style["z-index"] = zIndex + 1;
                    });

                object.boxDiv = boxDiv; // Dirty, but all is encapsulated by the debug utility
                object.pointDiv = boxDiv;

            } else {
                object.transform.xyz = pos[0];

                object.boxDiv.innerText = text;
                object.boxDiv.style.opacity = opacity;
                object.boxDiv.style.background = cssColor(fillColor);
                object.boxDiv.style.color = cssColor(color);

                var border = lineWidth + "px solid " + cssColor(color);
                object.boxDiv.style.border = border;
                object.pointDiv.style.border = border;
            }

            this._reset();

            return this;
        };

        this.sphere = function () {

            var _id = id || "__debugSphere";

            var object = getScene().objects[_id];

            if (!object) {

                new XEO.GameObject(getScene(), {
                    id: _id,
                    transform: new XEO.Translate({
                        xyz: pos[0]
                    }),
                    geometry: new XEO.SphereGeometry({
                        radius: radius
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: opacity < 1.0,
                        backfaces: false
                    }),
                    material: new XEO.PhongMaterial({ // Hides the sphere while still rendering it
                        diffuse: color,
                        emissive: color,
                        opacity: opacity,
                        specular: [1, 1, 1]
                    }),
                    visibility: new XEO.Visibility({  // This GameObject should not be pickable
                        visible: true
                    })
                });

            } else {
                object.transform.xyz = pos[0];
                object.geometry.radius = radius;
                object.material.color = color;
                object.material.emissive = color;
                object.material.opacity = opacity;
                object.modes.transparency = opacity < 1.0;
                object.visibility.visible = true;
            }

            this._reset();

            return this;
        };

        //---------------------------
        // Work in progress
        //---------------------------
        this.label3d = function () {

            var _id = id || "__debugLabel3d";

            var object = getScene().objects[_id];

            if (!object) {

                object = new XEO.GameObject(getScene(), {
                    id: _id,
                    transform: new XEO.Translate({
                        xyz: pos[0]
                    }),
                    geometry: new XEO.VectorTextGeometry({
                        text: text
                    }),
                    modes: new XEO.Modes({  // This GameObject should not be pickable
                        picking: false,
                        transparent: true
                    }),
                    material: new XEO.PhongMaterial({ // Hides the sphere while still rendering it
                        emissive: color,
                        opacity: opacity,
                        lineWidth: lineWidth
                    }),
                    billboard: new XEO.Billboard({
                        spherical: true
                    }),
                    visbility: new XEO.Visibility({  // This GameObject should not be pickable
                        visible: true
                    })
                });

            } else {
                object.transform.xyz = pos[0];
                object.geometry.text = text;
                object.material.emissive = color;
                object.material.opacity = opacity;
                object.modes.transparency = opacity < 1.0;
                object.material.lineWidth = lineWidth;
                object.visibility.visible = true;
            }

            this._reset();

            return this;
        };

        this._reset();
    })();

})();
<|endoftext|>


<commit_before>'use strict';

module.exports = function (N) {

  N.wire.once('init:parser', function sanitizer_plugin_init() {
    N.parse.addPlugin(
      'sanitizer',
      require('nodeca.core/lib/parser/plugins/sanitizer')(N, N.config.parser.sanitizer)
    );
  });
};
<commit_msg>Disable sanitizer<commit_after>'use strict';

module.exports = function (N) {

// Sanitizer disabled, because the output of the markdown parser is already
// trusted, so sanitizer does not gain any additional security.
/*
  N.wire.once('init:parser', function sanitizer_plugin_init() {
    N.parse.addPlugin(
      'sanitizer',
      require('nodeca.core/lib/parser/plugins/sanitizer')(N, N.config.parser.sanitizer)
    );
  });
*/
};
<|endoftext|>


<commit_before>var app = angular.module('app', ['ngRoute', 'ngResource', 'ngAnimate', 'ngOpbeat'])

app.config(function ($routeProvider) {
  var routeConfig = {
    controller: 'MainCtrl',
    templateUrl: 'main_ctrl.html'
  }

  $routeProvider
    .when('/', routeConfig)
    .otherwise({
      redirectTo: '/'
    })
})

app.config(function ($opbeatProvider) {
  $opbeatProvider.config({
    debug: true,
    orgId: '7f9fa667d0a349dd8377bc740bcfc33e',
    appId: '0a8757798e',
    performance: {
      enable: true,
      enableStackFrames: true
    }
  })
})
app.directive('customDirective', function () {
  return {
    template: '

',
    link: function link (scope, element, attrs, controller, transcludeFn) {
      scope.test = 'customDirective'
    }
  }
})

app.controller('MainCtrl', function mainCtrl ($scope, $http, $resource) {
  $scope.confirmation = function (conf) {
    $scope.confirmation = conf
  }
  var i = 0
  $scope.repeatArray = [10]

  setTimeout(function () {
    $scope.repeatArray.push(i++)
    $scope.$apply()
  }, 0)

  $http.get('confirmation.json').then(function (response) {
    $scope.confirmation(response.data)
  }, function () {
    throw new Error('Confirmation failed.')
  })
})
function startApp () {
  angular.bootstrap(document, ['app'])
}

startApp()

module.exports = {}
<commit_msg>add big datasets to e2e_test/playground<commit_after>var app = angular.module('app', ['ngRoute', 'ngResource', 'ngAnimate', 'ngOpbeat'])

app.config(function ($routeProvider) {
  var routeConfig = {
    controller: 'MainCtrl',
    templateUrl: 'main_ctrl.html'
  }

  $routeProvider
    .when('/', routeConfig)
    .otherwise({
      redirectTo: '/'
    })
})

app.config(function ($opbeatProvider) {
  $opbeatProvider.config({
    debug: true,
    orgId: '7f9fa667d0a349dd8377bc740bcfc33e',
    appId: '0a8757798e',
    performance: {
      enable: true,
      enableStackFrames: true
    }
  })
})
app.directive('customDirective', function () {
  return {
    template: '

',
    link: function link (scope, element, attrs, controller, transcludeFn) {
      scope.test = 'customDirective'
    }
  }
})
function exponentialize (seed, times) {
  var res = seed
  for (var j = 0; j < times; j++) {
    res = res.concat(res)
  }
  return res
}

app.controller('MainCtrl', function mainCtrl ($scope, $http, $resource) {
  $scope.confirmation = function (conf) {
    $scope.confirmation = conf
  }

  var repeatArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

  $scope.repeatArray = repeatArray
  setTimeout(function () {
    $scope.repeatArray.push(1)
    $scope.$apply()
  }, 0)

  $http.get('confirmation.json').then(function (response) {
    $scope.confirmation(response.data)
    $scope.repeatArray = exponentialize(repeatArray, 8)
  }, function () {
    throw new Error('Confirmation failed.')
  })
})
function startApp () {
  angular.bootstrap(document, ['app'])
}

startApp()

module.exports = {}
<|endoftext|>


<commit_before>import imperative from './imperative';

const elementProto = HTMLElement && HTMLElement.prototype || {};

export default imperative(
  elementProto.matches
  || elementProto.webkitMatchesSelector
  || elementProto.mozMatchesSelector
  || elementProto.msMatchesSelector
  || function(selector) {
    return Array.prototype.indexOf.call(document.querySelectorAll(selector), this) !== -1;
  }
);
<commit_msg>Removed fallback implementation of matches<commit_after>import imperative from './imperative';

const elementProto = HTMLElement && HTMLElement.prototype || {};

export default imperative(
  elementProto.matches
  || elementProto.webkitMatchesSelector
  || elementProto.mozMatchesSelector
  || elementProto.msMatchesSelector
);
<|endoftext|>


<commit_before>this.DeviceEditor = (function(){
  'use strict';

  var DeviceEditor = function DeviceEditor() {

    this.service = (new SW.Card()).services("inventory");

    // modified from https://datatables.net/reference/option/dom
    var editorLayout =
      "<'row'<'col-sm-12'fBi>>" +
      "<'row'<'col-sm-12'rt>>" +
      "<'row'<'col-sm-12'p>>" ;

    this.defaultEditorOptions = {idSrc: "id", formOptions: {main: {submit: "changed", drawType: "full-hold", title: "Edit Device(s)"}}};
    this.defaultTableOptions = {pageLength: 15, select: true, dom: editorLayout};
    this.editorOptions = undefined;
    this.tableOptions = undefined;
    this.editor = undefined;

    this.adminDefinedColumns = undefined;
    this.standardColumns = [
      {name: "name", label: "Name", type: "text", orderable: true, searchable: true},
      {name: "ip_address", label: "IP Address", type: "text", orderable: true, searchable: false},
      {name: "serial_number", label: "Serial Number", type: "text", orderable: false, searchable: false},
      {name: "mac_address", label: "MAC Address", type: "text", orderable: false, searchable: false},
      {name: "asset_tag", label: "Asset Tag", type: "text", orderable: false, searchable: false},
      {name: "device_type", label: "Device Type", type: "text", orderable: true, searchable: false},
      {name: "description", label: "Description", type: "text", orderable: false, searchable: false},
      {name: "manufacturer", label: "Manufacturer", type: "text", orderable: true, searchable: true},
      {name: "model", label: "Model", type: "text", orderable: true, searchable: true},
      {name: "location", label: "Location", type: "text", orderable: false, searchable: false},
      {name: "site", label: "Site", type: "text", orderable: false, searchable: false}, // must be translated to/from site.name
      {name: "owner", label: "Owner", type: "enum", orderable: false, searchable: false}, // must be translated to/from user objects
      {name: "purchase_date", label: "Purchase Date", type: "date", orderable: false, searchable: false},
      {name: "purchase_price", label: "Purchase Price", type: "text", orderable: false, searchable: false},
    ];

    this.users = undefined;
    this.loadAllUsers = function(prevMeta) {
      // prevMeta is undefined on the first call
      prevMeta = _.extend({current_page: 0, page_count: 1}, prevMeta);
      var page = prevMeta.current_page + 1;
      if (page > prevMeta.page_count) {
        return;
      }
      console.log("API load users page=" + page);
      var that = this;
      return (new SW.Card()).services("people")
        .request("people", {page: page, per_page: 5})
        .then(function(response){
          // create "hash" of {"First Last": id}
          var users = _.inject(response.people, function(hash, person) {
            hash[person.first_name.concat(" ", person.last_name)] = person.id;
            return hash;
          }, {});
          // merge with existing
          that.users = _.extend({}, that.users, users);
          return response.meta;
        })
        .then(this.loadAllUsers.bind(this));
    };

    this.initialResponse = undefined;
    this.loadDeviceMeta = function() {
      var that = this;
      return this.service
        .request("devices", {sort: [{name: "asc"}]})
        .then(function(response){
          that.initialResponse = response;
          that.adminDefinedColumns = response.meta.admin_defined_attrs || [];
        });
    };

    this.errorResponseToString = function errorResponseToString(error) {
      if (_.isArray(error)) {
        return _.collect(error, errorResponseToString).join(", ");
      }
      if (_.isObject(error) && error.errors) {
        return errorResponseToString(error.errors);
      }
      var result = "";
      if (error.title) {
        result = result.concat(error.title, ": ");
      }
      if (error.details) {
        result = result.concat(error.details);
      }
      else if (typeof(error) === "string") {
        result = result.concat(error);
      }
      else {
        result = result.concat(JSON.stringify(error));
      }
      return result;
    };

    this.editorAjaxAdapter = function(method, url, data, callback, xhrErrorCallback) {
      if (data.action !== "edit") {
        xhrErrorCallback(null, "error", "invalid");
        return;
      }
      var that = this;
      var requests = _.collect(data.data, function(updates, id) {
        var d = $.Deferred();
        console.log("API updates for device " + id + ": " + JSON.stringify(updates));
        that.service
          .request("device:update", parseInt(id, 10), updates)
          .then(
            function(response){
              d.resolve(response);
            },
            function(response){
              console.warn("API device " + id + " update error: ", that.errorResponseToString(response));
              d.reject(response);
            }
          );
        return d.promise();
      });
      $.when.apply($, requests).then(
        function(args) {
          var devices = _.toArray(arguments);
          callback({data: devices});
        },
        function(args) {
          var errors = _.collect(_.toArray(arguments), that.errorResponseToString).join(", ");
          callback({error: errors});
        }
      );
    };

    this.getEditorField = function(columnConfig, accessor) {
      var type;
      switch(columnConfig.type) {
        case "enum":
          type = "select";
          break;
        case "date":
          type = "date";
          break;
        default:
          type = "text";
      }
      var options = null;
      if (columnConfig.name === "owner") {
        options = this.users;
      }
      var field = _.pick(columnConfig, "name", "label");
      _.extend(field, {type: type, data: accessor, options: options});
      return field;
    };

    this.standardAttributeEditorAccessor = function(columnConfig, data, type, value) {
      if (type !== "set") {
        return this.standardAttributeTableRenderer(columnConfig, data);
      }
    };

    this.customAttributeEditorAccessor = function(columnConfig, data, type, value) {
      if (type !== "set") {
        return this.customAttributeTableRenderer(columnConfig, data);
      }
    };

    this.getEditorFields = function() {
      var that = this;
      var fields = [];
      _.each(this.standardColumns, function(columnConfig) {
        fields.push(that.getEditorField(columnConfig, that.standardAttributeEditorAccessor.bind(that, columnConfig)));
      });
      _.each(this.adminDefinedColumns, function(columnConfig) {
        fields.push(that.getEditorField(columnConfig, that.customAttributeEditorAccessor.bind(that, columnConfig)));
      });
      return fields;
    };

    this.configureEditor = function(selector, editorOptions) {
      this.editorOptions = _.extend(this.defaultEditorOptions,
        {table: selector, fields: this.getEditorFields()},
        editorOptions,
        {ajax: this.editorAjaxAdapter.bind(this)});
      return this.editorOptions;
    };

    this.createEditor = function(editorOptions) {
      this.editor = new $.fn.dataTable.Editor(editorOptions);
    };

    this.standardAttributeTableRenderer = function(columnConfig, data, requestType, row, meta) {
      var value = data[columnConfig.name] || "";
      if (columnConfig.name === "site") {
        if (data["site"] && data["site"].name) {
          value = data["site"].name;
        }
      }
      else if (columnConfig.name === "owner") {
        if (data["owner"] && data["owner"].id) {
          value = data["owner"].first_name + " " + data["owner"].last_name;
        }
        else if (data["primary_owner_name"]) {
          value = data["primary_owner_name"];
        }
      }
      return value;
    };

    this.customAttributeTableRenderer = function(columnConfig, data, requestType, row, meta) {
      return data.admin_defined_attrs[columnConfig.name] || "";
    };

    this.getTableColumn = function(columnConfig, renderer) {
      var type;
      switch(columnConfig.type) {
        case "date":
          type = "date";
          break;
        default:
          type = "string";
      }
      var column = _.pick(columnConfig, "visible", "orderable", "searchable");
      _.extend(column, {title: columnConfig.label, type: type, data: null, render: renderer});
      _.defaults(column, {orderable: false, searchable: false});
      return column;
    };

    this.getTableColumns = function() {
      var that = this;
      var columns = [];
      _.each(this.standardColumns, function(columnConfig) {
        columns.push(that.getTableColumn(columnConfig, that.standardAttributeTableRenderer.bind(that, columnConfig)));
      });
      _.each(this.adminDefinedColumns, function(columnConfig) {
        columns.push(that.getTableColumn(columnConfig, that.customAttributeTableRenderer.bind(that, columnConfig)));
      });
      return columns;
    }

    this.tableAjaxAdapter = function(data, callback, settings) {
      var that = this;
      var page = 1 + data.start / this.tableOptions.pageLength;
      this.service
        .request("devices", {sort: [{name: "asc"}], page: page, per_page: data.length})
        .then(
          function(response){
            callback({draw: data.draw,
              data: response.devices,
              recordsTotal: that.initialResponse.meta.total_entries,
              recordsFiltered: response.meta.total_entries
            });
          },
          function(response){
            var error = that.errorResponseToString(response);
            console.warn("API devices fetch error: ", error);
            callback({draw: data.draw, error: error});
          }
        );
    };

    this.configureTable = function(tableOptions) {
      this.tableOptions = _.extend(this.defaultTableOptions,
        tableOptions,
        { columns: this.getTableColumns(),
          serverSide: true, ajax: this.tableAjaxAdapter.bind(this),
          data: this.initialResponse.devices,
          deferLoading: [this.initialResponse.meta.total_entries, this.initialResponse.meta.total_entries],
          buttons: [{extend: "edit", editor: this.editor}]
        }
      );
      return this.tableOptions;
    };

    this.createTable = function(selector, tableOptions) {
      $(selector).DataTable(tableOptions);
    };

    this.load = function(selector, editorOptions, tableOptions) {
      var that = this;
      return this.loadAllUsers.call(this)
        .then(this.loadDeviceMeta.bind(this))
        .then(this.configureEditor.bind(this, selector, editorOptions))
        .then(this.createEditor.bind(this))
        .then(this.configureTable.bind(this, tableOptions))
        .then(this.createTable.bind(this, selector));
    }
  };

  return DeviceEditor;

})();

<commit_msg>handle sort on ajax call<commit_after>this.DeviceEditor = (function(){
  'use strict';

  var DeviceEditor = function DeviceEditor() {

    this.service = (new SW.Card()).services("inventory");

    // modified from https://datatables.net/reference/option/dom
    var editorLayout =
      "<'row'<'col-sm-12'fBi>>" +
      "<'row'<'col-sm-12'rt>>" +
      "<'row'<'col-sm-12'p>>" ;

    this.defaultEditorOptions = {idSrc: "id", formOptions: {main: {submit: "changed", drawType: "full-hold", title: "Edit Device(s)"}}};
    this.defaultTableOptions = {pageLength: 15, select: true, dom: editorLayout};
    this.editorOptions = undefined;
    this.tableOptions = undefined;
    this.editor = undefined;

    this.adminDefinedColumns = undefined;
    this.standardColumns = [
      {name: "name", label: "Name", type: "text", orderable: true, searchable: true},
      {name: "ip_address", label: "IP Address", type: "text", orderable: true, searchable: false},
      {name: "serial_number", label: "Serial Number", type: "text", orderable: false, searchable: false},
      {name: "mac_address", label: "MAC Address", type: "text", orderable: false, searchable: false},
      {name: "asset_tag", label: "Asset Tag", type: "text", orderable: false, searchable: false},
      {name: "device_type", label: "Device Type", type: "text", orderable: true, searchable: false},
      {name: "description", label: "Description", type: "text", orderable: false, searchable: false},
      {name: "manufacturer", label: "Manufacturer", type: "text", orderable: true, searchable: true},
      {name: "model", label: "Model", type: "text", orderable: true, searchable: true},
      {name: "location", label: "Location", type: "text", orderable: false, searchable: false},
      {name: "site", label: "Site", type: "text", orderable: false, searchable: false}, // must be translated to/from site.name
      {name: "owner", label: "Owner", type: "enum", orderable: false, searchable: false}, // must be translated to/from user objects
      {name: "purchase_date", label: "Purchase Date", type: "date", orderable: false, searchable: false},
      {name: "purchase_price", label: "Purchase Price", type: "text", orderable: false, searchable: false},
    ];

    this.users = undefined;
    this.loadAllUsers = function(prevMeta) {
      // prevMeta is undefined on the first call
      prevMeta = _.extend({current_page: 0, page_count: 1}, prevMeta);
      var page = prevMeta.current_page + 1;
      if (page > prevMeta.page_count) {
        return;
      }
      console.log("API load users page=" + page);
      var that = this;
      return (new SW.Card()).services("people")
        .request("people", {page: page, per_page: 5})
        .then(function(response){
          // create "hash" of {"First Last": id}
          var users = _.inject(response.people, function(hash, person) {
            hash[person.first_name.concat(" ", person.last_name)] = person.id;
            return hash;
          }, {});
          // merge with existing
          that.users = _.extend({}, that.users, users);
          return response.meta;
        })
        .then(this.loadAllUsers.bind(this));
    };

    this.initialResponse = undefined;
    this.loadDeviceMeta = function() {
      var that = this;
      return this.service
        .request("devices", {sort: [{name: "asc"}]})
        .then(function(response){
          that.initialResponse = response;
          that.adminDefinedColumns = response.meta.admin_defined_attrs || [];
        });
    };

    this.errorResponseToString = function errorResponseToString(error) {
      if (_.isArray(error)) {
        return _.collect(error, errorResponseToString).join(", ");
      }
      if (_.isObject(error) && error.errors) {
        return errorResponseToString(error.errors);
      }
      var result = "";
      if (error.title) {
        result = result.concat(error.title, ": ");
      }
      if (error.details) {
        result = result.concat(error.details);
      }
      else if (typeof(error) === "string") {
        result = result.concat(error);
      }
      else {
        result = result.concat(JSON.stringify(error));
      }
      return result;
    };

    this.editorAjaxAdapter = function(method, url, data, callback, xhrErrorCallback) {
      if (data.action !== "edit") {
        xhrErrorCallback(null, "error", "invalid");
        return;
      }
      var that = this;
      var requests = _.collect(data.data, function(updates, id) {
        var d = $.Deferred();
        console.log("API updates for device " + id + ": " + JSON.stringify(updates));
        that.service
          .request("device:update", parseInt(id, 10), updates)
          .then(
            function(response){
              d.resolve(response);
            },
            function(response){
              console.warn("API device " + id + " update error: ", that.errorResponseToString(response));
              d.reject(response);
            }
          );
        return d.promise();
      });
      $.when.apply($, requests).then(
        function(args) {
          var devices = _.toArray(arguments);
          callback({data: devices});
        },
        function(args) {
          var errors = _.collect(_.toArray(arguments), that.errorResponseToString).join(", ");
          callback({error: errors});
        }
      );
    };

    this.getEditorField = function(columnConfig, accessor) {
      var type;
      switch(columnConfig.type) {
        case "enum":
          type = "select";
          break;
        case "date":
          type = "date";
          break;
        default:
          type = "text";
      }
      var options = null;
      if (columnConfig.name === "owner") {
        options = this.users;
      }
      var field = _.pick(columnConfig, "name", "label");
      _.extend(field, {type: type, data: accessor, options: options});
      return field;
    };

    this.standardAttributeEditorAccessor = function(columnConfig, data, type, value) {
      if (type !== "set") {
        return this.standardAttributeTableRenderer(columnConfig, data);
      }
    };

    this.customAttributeEditorAccessor = function(columnConfig, data, type, value) {
      if (type !== "set") {
        return this.customAttributeTableRenderer(columnConfig, data);
      }
    };

    this.getEditorFields = function() {
      var that = this;
      var fields = [];
      _.each(this.standardColumns, function(columnConfig) {
        fields.push(that.getEditorField(columnConfig, that.standardAttributeEditorAccessor.bind(that, columnConfig)));
      });
      _.each(this.adminDefinedColumns, function(columnConfig) {
        fields.push(that.getEditorField(columnConfig, that.customAttributeEditorAccessor.bind(that, columnConfig)));
      });
      return fields;
    };

    this.configureEditor = function(selector, editorOptions) {
      this.editorOptions = _.extend(this.defaultEditorOptions,
        {table: selector, fields: this.getEditorFields()},
        editorOptions,
        {ajax: this.editorAjaxAdapter.bind(this)});
      return this.editorOptions;
    };

    this.createEditor = function(editorOptions) {
      this.editor = new $.fn.dataTable.Editor(editorOptions);
    };

    this.standardAttributeTableRenderer = function(columnConfig, data, requestType, row, meta) {
      var value = data[columnConfig.name] || "";
      if (columnConfig.name === "site") {
        if (data["site"] && data["site"].name) {
          value = data["site"].name;
        }
      }
      else if (columnConfig.name === "owner") {
        if (data["owner"] && data["owner"].id) {
          value = data["owner"].first_name + " " + data["owner"].last_name;
        }
        else if (data["primary_owner_name"]) {
          value = data["primary_owner_name"];
        }
      }
      return value;
    };

    this.customAttributeTableRenderer = function(columnConfig, data, requestType, row, meta) {
      return data.admin_defined_attrs[columnConfig.name] || "";
    };

    this.getTableColumn = function(columnConfig, renderer) {
      var type;
      switch(columnConfig.type) {
        case "date":
          type = "date";
          break;
        default:
          type = "string";
      }
      var column = _.pick(columnConfig, "name", "visible", "orderable", "searchable");
      _.extend(column, {title: columnConfig.label, type: type, data: null, render: renderer});
      _.defaults(column, {orderable: false, searchable: false});
      return column;
    };

    this.getTableColumns = function() {
      var that = this;
      var columns = [];
      _.each(this.standardColumns, function(columnConfig) {
        columns.push(that.getTableColumn(columnConfig, that.standardAttributeTableRenderer.bind(that, columnConfig)));
      });
      _.each(this.adminDefinedColumns, function(columnConfig) {
        columns.push(that.getTableColumn(columnConfig, that.customAttributeTableRenderer.bind(that, columnConfig)));
      });
      return columns;
    }

    this.tableAjaxAdapter = function(data, callback, settings) {
      var that = this;
      var page = 1 + data.start / this.tableOptions.pageLength;
      var sort = {};
      if (data.order && data.order[0]) {
        sort[settings.aoColumns[data.order[0].column].sName] = data.order[0].dir;
      }
      else {
        sort["name"] = "asc";
      }
      this.service
        .request("devices", {sort: [sort], page: page, per_page: data.length})
        .then(
          function(response){
            callback({draw: data.draw,
              data: response.devices,
              recordsTotal: that.initialResponse.meta.total_entries,
              recordsFiltered: response.meta.total_entries
            });
          },
          function(response){
            var error = that.errorResponseToString(response);
            console.warn("API devices fetch error: ", error);
            callback({draw: data.draw, error: error});
          }
        );
    };

    this.configureTable = function(tableOptions) {
      this.tableOptions = _.extend(this.defaultTableOptions,
        tableOptions,
        { columns: this.getTableColumns(),
          serverSide: true, ajax: this.tableAjaxAdapter.bind(this),
          data: this.initialResponse.devices,
          deferLoading: [this.initialResponse.meta.total_entries, this.initialResponse.meta.total_entries],
          buttons: [{extend: "edit", editor: this.editor}]
        }
      );
      return this.tableOptions;
    };

    this.createTable = function(selector, tableOptions) {
      $(selector).DataTable(tableOptions);
    };

    this.load = function(selector, editorOptions, tableOptions) {
      var that = this;
      return this.loadAllUsers.call(this)
        .then(this.loadDeviceMeta.bind(this))
        .then(this.configureEditor.bind(this, selector, editorOptions))
        .then(this.createEditor.bind(this))
        .then(this.configureTable.bind(this, tableOptions))
        .then(this.createTable.bind(this, selector));
    }
  };

  return DeviceEditor;

})();

<|endoftext|>


<commit_before>import Emitter from 'component-emitter';

import { name as NAME, version as VERSION } from '../package.json';
import dataAttributesToObject from './data-attributes-to-object';
import interval from './interval';

const INIT = 'init';
const START = 'start';
const TYPE = 'type';
const LOOP = 'loop';
const STOP = 'stop';
const DESTROY = 'destroy';

const EVENTS = {
  INIT,
  START,
  TYPE,
  LOOP,
  STOP,
  DESTROY,
};

const DATA_ATTRIBUTES = [
  'text',
  'interval',
  'auto-start',
  'loop',
  'loop-interval',
  'empty',
];

const DEFAULTS = {
  text: 'This is the default text.',
  interval: [200, 300],
  autoStart: true,
  loop: false,
  loopInterval: 0,
  empty: '\u00A0',
};

const autotyper = Object.assign(new Emitter(), {
  init(...args) {
    const [
      element = null,
      options = {},
    ] = this.parseArguments(args);

    if (element) {
      const text = element.innerHTML.trim();
      const dataOptions = Object.assign(
        dataAttributesToObject(element, DATA_ATTRIBUTES, NAME),
        JSON.parse(element.getAttribute(`data-${NAME}-options`)),
      );

      this.element = element;

      this.settings = Object.assign(
        {},
        DEFAULTS,
        (text && { text }),
        dataOptions,
        options,
      );

      Object.defineProperty(this, 'text', {
        set(value) {
          if (this.element) {
            this.element.innerHTML = value;
          }
        },
      });
    } else {
      this.settings = Object.assign({}, DEFAULTS, options);
    }

    this.isRunning = false;

    this.reset();

    this.emit(INIT);

    if (this.settings.autoStart === true) {
      this.start();
    } else {
      const delay = parseInt(this.settings.autoStart, 10);

      if (!isNaN(delay)) {
        setTimeout(() => this.start(), delay);
      }
    }

    return this;
  },
  parseArguments(args) {
    if (args.length === 0) {
      return args;
    }

    const [firstArg] = args;

    if (process.env.NODE_ENV === 'test') {
      if (firstArg instanceof window.HTMLElement) {
        return args;
      }

      return [null, firstArg];
    }

    if (firstArg instanceof HTMLElement) {
      return args;
    }

    return [null, firstArg];
  },
  reset() {
    this.letterTotal = this.settings.text.length;
    this.letterCount = 0;

    if (this.settings.loop) {
      if (typeof this.settings.loop === 'number') {
        this.loopTotal = this.settings.loop;
      }

      this.loopCount = 0;
    }

    return this;
  },
  start() {
    if (this.isRunning) {
      return this;
    }

    clearTimeout(this.timeout);

    this.isRunning = true;

    this.emit(START);

    this.type();

    return this;
  },
  type() {
    let text;

    this.tick(interval(this.settings.interval));

    if (this.letterCount > this.letterTotal) {
      if (this.settings.loop) {
        return this.loop();
      }

      return this.stop();
    }

    if (this.letterCount === 0) {
      if (this.settings.empty === false) {
        this.letterCount += 1;

        text = this.settings.text.substring(0, this.letterCount);
      } else if (typeof this.settings.empty === 'string') {
        text = this.settings.empty;
      }
    } else {
      text = this.settings.text.substring(0, this.letterCount);
    }

    this.text = text;

    this.emit(TYPE, text);

    this.letterCount += 1;

    return this;
  },
  stop() {
    if (!this.isRunning) {
      return this;
    }

    clearTimeout(this.timeout);

    this.isRunning = false;

    this.emit(STOP);

    return this;
  },
  destroy() {
    if (this.isRunning) {
      this.stop();
    }

    this.emit(DESTROY);

    this.off();

    this.element = null;
  },
  tick(duration) {
    clearTimeout(this.timeout);

    this.timeout = setTimeout(() => this.type(), duration);

    return this;
  },
  loop() {
    this.tick(interval(this.settings.loopInterval));

    if (this.loopCount === this.loopTotal) {
      return this.stop();
    }

    this.loopCount += 1;

    this.letterCount = 0;

    this.emit(LOOP, this.loopCount);

    return this;
  },
});

export {
  autotyper as default,
  DATA_ATTRIBUTES,
  DEFAULTS,
  EVENTS,
  NAME,
  VERSION,
};
<commit_msg>feat(autotyper): add character param to TYPE event<commit_after>import Emitter from 'component-emitter';

import { name as NAME, version as VERSION } from '../package.json';
import dataAttributesToObject from './data-attributes-to-object';
import interval from './interval';

const INIT = 'init';
const START = 'start';
const TYPE = 'type';
const LOOP = 'loop';
const STOP = 'stop';
const DESTROY = 'destroy';

const EVENTS = {
  INIT,
  START,
  TYPE,
  LOOP,
  STOP,
  DESTROY,
};

const DATA_ATTRIBUTES = [
  'text',
  'interval',
  'auto-start',
  'loop',
  'loop-interval',
  'empty',
];

const DEFAULTS = {
  text: 'This is the default text.',
  interval: [200, 300],
  autoStart: true,
  loop: false,
  loopInterval: 0,
  empty: '\u00A0',
};

const autotyper = Object.assign(new Emitter(), {
  init(...args) {
    const [
      element = null,
      options = {},
    ] = this.parseArguments(args);

    if (element) {
      const text = element.innerHTML.trim();
      const dataOptions = Object.assign(
        dataAttributesToObject(element, DATA_ATTRIBUTES, NAME),
        JSON.parse(element.getAttribute(`data-${NAME}-options`)),
      );

      this.element = element;

      this.settings = Object.assign(
        {},
        DEFAULTS,
        (text && { text }),
        dataOptions,
        options,
      );

      Object.defineProperty(this, 'text', {
        set(value) {
          if (this.element) {
            this.element.innerHTML = value;
          }
        },
      });
    } else {
      this.settings = Object.assign({}, DEFAULTS, options);
    }

    this.isRunning = false;

    this.reset();

    this.emit(INIT);

    if (this.settings.autoStart === true) {
      this.start();
    } else {
      const delay = parseInt(this.settings.autoStart, 10);

      if (!isNaN(delay)) {
        setTimeout(() => this.start(), delay);
      }
    }

    return this;
  },
  parseArguments(args) {
    if (args.length === 0) {
      return args;
    }

    const [firstArg] = args;

    if (process.env.NODE_ENV === 'test') {
      if (firstArg instanceof window.HTMLElement) {
        return args;
      }

      return [null, firstArg];
    }

    if (firstArg instanceof HTMLElement) {
      return args;
    }

    return [null, firstArg];
  },
  reset() {
    this.letterTotal = this.settings.text.length;
    this.letterCount = 0;

    if (this.settings.loop) {
      if (typeof this.settings.loop === 'number') {
        this.loopTotal = this.settings.loop;
      }

      this.loopCount = 0;
    }

    return this;
  },
  start() {
    if (this.isRunning) {
      return this;
    }

    clearTimeout(this.timeout);

    this.isRunning = true;

    this.emit(START);

    this.type();

    return this;
  },
  type() {
    let text;
    let character;

    this.tick(interval(this.settings.interval));

    if (this.letterCount > this.letterTotal) {
      if (this.settings.loop) {
        return this.loop();
      }

      return this.stop();
    }

    if (this.letterCount === 0) {
      if (this.settings.empty === false) {
        this.letterCount += 1;

        text = this.settings.text.substring(0, this.letterCount);
        character = this.settings.text.substring(this.letterCount - 1, this.letterCount);
      } else if (typeof this.settings.empty === 'string') {
        text = this.settings.empty;
        character = this.settings.empty;
      }
    } else {
      text = this.settings.text.substring(0, this.letterCount);
      character = this.settings.text.substring(this.letterCount - 1, this.letterCount);
    }

    this.text = text;

    this.emit(TYPE, text, character);

    this.letterCount += 1;

    return this;
  },
  stop() {
    if (!this.isRunning) {
      return this;
    }

    clearTimeout(this.timeout);

    this.isRunning = false;

    this.emit(STOP);

    return this;
  },
  destroy() {
    if (this.isRunning) {
      this.stop();
    }

    this.emit(DESTROY);

    this.off();

    this.element = null;
  },
  tick(duration) {
    clearTimeout(this.timeout);

    this.timeout = setTimeout(() => this.type(), duration);

    return this;
  },
  loop() {
    this.tick(interval(this.settings.loopInterval));

    if (this.loopCount === this.loopTotal) {
      return this.stop();
    }

    this.loopCount += 1;

    this.letterCount = 0;

    this.emit(LOOP, this.loopCount);

    return this;
  },
});

export {
  autotyper as default,
  DATA_ATTRIBUTES,
  DEFAULTS,
  EVENTS,
  NAME,
  VERSION,
};
<|endoftext|>


<commit_before>import { gltfInput } from './input.js';

import { GltfView } from 'gltf-viewer-source';

import { UIModel } from './logic/uimodel.js';
import { app } from './ui/ui.js';
import { Observable, Subject, from, merge } from 'rxjs';
import { mergeMap, filter, map, multicast } from 'rxjs/operators';
import { gltfModelPathProvider, fillEnvironmentWithPaths } from './model_path_provider.js';

async function main()
{
    const canvas = document.getElementById("canvas");
    const context = canvas.getContext("webgl2", { alpha: false, antialias: true });
    const ui = document.getElementById("app");
    const view = new GltfView(context);
    const resourceLoader = view.createResourceLoader();
    const state = view.createState();

    resourceLoader.initDracoLib();
    resourceLoader.initKtxLib();

    const pathProvider = new gltfModelPathProvider('assets/models/2.0/model-index.json');
    await pathProvider.initialize();
    const environmentPaths = fillEnvironmentWithPaths({
        "footprint_court_512": "Foodprint Court (512p)",
        "footprint_court": "Foodprint Court",
        "pisa": "Pisa courtyard nearing sunset, Italy",
        "doge2": "Courtyard of the Doge's palace",
        "ennis": "Dining room of the Ennis-Brown House",
        "field": "Field",
        "helipad": "Helipad Goldenhour",
        "papermill": "Papermill Ruins",
        "neutral": "Studio Neutral",
        "chromatic": "Studio Chromatic",
        "directional": "Studio Directional",
    }, "assets/environments/");

    const uiModel = new UIModel(app, pathProvider, environmentPaths);

    // whenever a new model is selected, load it and when complete pass the loaded gltf
    // into a stream back into the UI
    const gltfLoadedSubject = new Subject();
    const gltfLoadedMulticast = uiModel.model.pipe(
        mergeMap( (model) =>
        {
        	uiModel.goToLoadingState();
            return from(resourceLoader.loadGltf(model.mainFile, model.additionalFiles).then( (gltf) => {
                state.gltf = gltf;
                const defaultScene = state.gltf.scene;
                state.sceneIndex = defaultScene === undefined ? 0 : defaultScene;
                state.cameraIndex = undefined;
                const scene = state.gltf.scenes[state.sceneIndex];
                scene.applyTransformHierarchy(state.gltf);
                state.userCamera.aspectRatio = canvas.width / canvas.height;
                state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);

                // Try to start as many animations as possible without generating conficts.
                state.animationIndices = [];
                for (let i = 0; i < gltf.animations.length; i++)
                {
                    if (!gltf.nonDisjointAnimations(state.animationIndices).includes(i))
                    {
                        state.animationIndices.push(i);
                    }
                }
                state.animationTimer.start();

                uiModel.exitLoadingState();

                return state;
            })
            );
        }),
        // transform gltf loaded observable to multicast observable to avoid multiple execution with multiple subscriptions
        multicast(gltfLoadedSubject)
    );

    uiModel.disabledAnimations(uiModel.activeAnimations.pipe(map(animationIndices => {
        // Disable all animations which are not disjoint to the current selection of animations.
        return state.gltf.nonDisjointAnimations(animationIndices);
    })));

    const sceneChangedSubject = new Subject();
    const sceneChangedObservable = uiModel.scene.pipe(map( newSceneIndex => {
        state.sceneIndex = newSceneIndex;
        state.cameraIndex = undefined;
        const scene = state.gltf.scenes[state.sceneIndex];
        scene.applyTransformHierarchy(state.gltf);
        state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);
    }),
    multicast(sceneChangedSubject)
    );

    const statisticsUpdateObservableTemp = merge(
        gltfLoadedMulticast,
        sceneChangedObservable
    );

    const statisticsUpdateObservable = statisticsUpdateObservableTemp.pipe(
        map( (_) => view.gatherStatistics(state) )
    );

    const cameraExportChangedObservable = uiModel.cameraValuesExport.pipe( map(_ => {
        let camera = state.userCamera;
        if(state.cameraIndex !== undefined)
        {
            camera = state.gltf.cameras[state.cameraIndex];
        }
        const cameraDesc = camera.getDescription(state.gltf);
        return cameraDesc;
    }));
    cameraExportChangedObservable.subscribe( cameraDesc => {
        uiModel.copyToClipboard(JSON.stringify(cameraDesc));
    });

    uiModel.captureCanvas.subscribe( () => {
        view.renderFrame(state, canvas.width, canvas.height);
        const dataURL = canvas.toDataURL();

        var element = document.createElement('a');
        element.setAttribute('href', dataURL);
        element.setAttribute('download', "capture.png");

        element.style.display = 'none';
        document.body.appendChild(element);

        element.click();

        document.body.removeChild(element);
    });

    uiModel.camera.pipe(filter(camera => camera === -1)).subscribe( () => {
        state.cameraIndex = undefined;
    });
    uiModel.camera.pipe(filter(camera => camera !== -1)).subscribe( camera => {
        state.cameraIndex = camera;
    });

    uiModel.variant.subscribe( variant => {
        state.variant = variant;
    });

    uiModel.tonemap.subscribe( tonemap => {
        state.renderingParameters.toneMap = tonemap;
    });

    uiModel.debugchannel.subscribe( debugchannel => {
        state.renderingParameters.debugOutput = debugchannel;
    });

    uiModel.skinningEnabled.subscribe( skinningEnabled => {
        state.renderingParameters.skinning = skinningEnabled;
    });

    uiModel.exposurecompensation.subscribe( exposurecompensation => {
        state.renderingParameters.exposure = Math.pow(2, exposurecompensation);
    });

    uiModel.morphingEnabled.subscribe( morphingEnabled => {
        state.renderingParameters.morphing = morphingEnabled;
    });

    uiModel.clearcoatEnabled.subscribe( clearcoatEnabled => {
        state.renderingParameters.clearcoat = clearcoatEnabled;
    });
    uiModel.sheenEnabled.subscribe( sheenEnabled => {
        state.renderingParameters.sheen = sheenEnabled;
    });
    uiModel.transmissionEnabled.subscribe( transmissionEnabled => {
        state.renderingParameters.transmission = transmissionEnabled;
    });

    uiModel.iblEnabled.subscribe( iblEnabled => {
        state.renderingParameters.useIBL = iblEnabled;
    });

    uiModel.renderEnvEnabled.subscribe( renderEnvEnabled => {
        state.renderingParameters.renderEnvironmentMap = renderEnvEnabled;
    });
    uiModel.blurEnvEnabled.subscribe( blurEnvEnabled => {
        state.renderingParameters.blurEnvironmentMap = blurEnvEnabled;
    });

    uiModel.punctualLightsEnabled.subscribe( punctualLightsEnabled => {
        state.renderingParameters.usePunctual = punctualLightsEnabled;
    });

    uiModel.environmentEnabled.subscribe( environmentEnabled => {
        state.renderingParameters.environmentBackground = environmentEnabled;
    });

    uiModel.environmentRotation.subscribe( environmentRotation => {
        switch (environmentRotation)
        {
        case "+Z":
            state.renderingParameters.environmentRotation = 90.0;
            break;
        case "-X":
            state.renderingParameters.environmentRotation = 180.0;
            break;
        case "-Z":
            state.renderingParameters.environmentRotation = 270.0;
            break;
        case "+X":
            state.renderingParameters.environmentRotation = 0.0;
            break;
        }
    });

    uiModel.clearColor.subscribe( clearColor => {
        state.renderingParameters.clearColor = clearColor;
    });

    uiModel.animationPlay.subscribe( animationPlay => {
        if(animationPlay)
        {
            state.animationTimer.unpause();
        }
        else
        {
            state.animationTimer.pause();
        }
    });

    uiModel.activeAnimations.subscribe( animations => {
        state.animationIndices = animations;
    });

    uiModel.hdr.subscribe( hdrFile => {
        resourceLoader.loadEnvironment(hdrFile).then( (environment) => {
            state.environment = environment;
        });
    });

    uiModel.attachGltfLoaded(gltfLoadedMulticast);
    uiModel.updateStatistics(statisticsUpdateObservable);
    const sceneChangedStateObservable = uiModel.scene.pipe(map( newSceneIndex => state));
    uiModel.attachCameraChangeObservable(sceneChangedStateObservable);
    gltfLoadedMulticast.connect();

    const input = new gltfInput(canvas);
    input.setupGlobalInputBindings(document);
    input.setupCanvasInputBindings(canvas);
    input.onRotate = (deltaX, deltaY) =>
    {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.orbit(deltaX, deltaY);
        }
    };
    input.onPan = (deltaX, deltaY) =>
    {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.pan(deltaX, deltaY);
        }
    };
    input.onZoom = (delta) =>
    {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.zoomBy(delta);
        }
    };

    // configure the animation loop
    const update = () =>
    {
        canvas.width = window.innerWidth - ui.getBoundingClientRect().width;
        canvas.height = canvas.clientHeight;

        view.renderFrame(state, canvas.width, canvas.height);
        window.requestAnimationFrame(update);
    };

    // After this start executing animation loop.
    window.requestAnimationFrame(update);
}

export { main };
<commit_msg>renamed environment maps for UI<commit_after>import { gltfInput } from './input.js';

import { GltfView } from 'gltf-viewer-source';

import { UIModel } from './logic/uimodel.js';
import { app } from './ui/ui.js';
import { Observable, Subject, from, merge } from 'rxjs';
import { mergeMap, filter, map, multicast } from 'rxjs/operators';
import { gltfModelPathProvider, fillEnvironmentWithPaths } from './model_path_provider.js';

async function main()
{
    const canvas = document.getElementById("canvas");
    const context = canvas.getContext("webgl2", { alpha: false, antialias: true });
    const ui = document.getElementById("app");
    const view = new GltfView(context);
    const resourceLoader = view.createResourceLoader();
    const state = view.createState();

    resourceLoader.initDracoLib();
    resourceLoader.initKtxLib();

    const pathProvider = new gltfModelPathProvider('assets/models/2.0/model-index.json');
    await pathProvider.initialize();
    const environmentPaths = fillEnvironmentWithPaths({
        "footprint_court_512": "Foodprint Court (512p)",
        "footprint_court": "Foodprint Court",
        "pisa": "Pisa",
        "doge2": "Doge's palace",
        "ennis": "Dining room",
        "field": "Field",
        "helipad": "Helipad Goldenhour",
        "papermill": "Papermill Ruins",
        "neutral": "Studio Neutral",
        "chromatic": "Studio Chromatic",
        "directional": "Studio Directional",
    }, "assets/environments/");

    const uiModel = new UIModel(app, pathProvider, environmentPaths);

    // whenever a new model is selected, load it and when complete pass the loaded gltf
    // into a stream back into the UI
    const gltfLoadedSubject = new Subject();
    const gltfLoadedMulticast = uiModel.model.pipe(
        mergeMap( (model) =>
        {
        	uiModel.goToLoadingState();
            return from(resourceLoader.loadGltf(model.mainFile, model.additionalFiles).then( (gltf) => {
                state.gltf = gltf;
                const defaultScene = state.gltf.scene;
                state.sceneIndex = defaultScene === undefined ? 0 : defaultScene;
                state.cameraIndex = undefined;
                const scene = state.gltf.scenes[state.sceneIndex];
                scene.applyTransformHierarchy(state.gltf);
                state.userCamera.aspectRatio = canvas.width / canvas.height;
                state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);

                // Try to start as many animations as possible without generating conficts.
                state.animationIndices = [];
                for (let i = 0; i < gltf.animations.length; i++)
                {
                    if (!gltf.nonDisjointAnimations(state.animationIndices).includes(i))
                    {
                        state.animationIndices.push(i);
                    }
                }
                state.animationTimer.start();

                uiModel.exitLoadingState();

                return state;
            })
            );
        }),
        // transform gltf loaded observable to multicast observable to avoid multiple execution with multiple subscriptions
        multicast(gltfLoadedSubject)
    );

    uiModel.disabledAnimations(uiModel.activeAnimations.pipe(map(animationIndices => {
        // Disable all animations which are not disjoint to the current selection of animations.
        return state.gltf.nonDisjointAnimations(animationIndices);
    })));

    const sceneChangedSubject = new Subject();
    const sceneChangedObservable = uiModel.scene.pipe(map( newSceneIndex => {
        state.sceneIndex = newSceneIndex;
        state.cameraIndex = undefined;
        const scene = state.gltf.scenes[state.sceneIndex];
        scene.applyTransformHierarchy(state.gltf);
        state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);
    }),
    multicast(sceneChangedSubject)
    );

    const statisticsUpdateObservableTemp = merge(
        gltfLoadedMulticast,
        sceneChangedObservable
    );

    const statisticsUpdateObservable = statisticsUpdateObservableTemp.pipe(
        map( (_) => view.gatherStatistics(state) )
    );

    const cameraExportChangedObservable = uiModel.cameraValuesExport.pipe( map(_ => {
        let camera = state.userCamera;
        if(state.cameraIndex !== undefined)
        {
            camera = state.gltf.cameras[state.cameraIndex];
        }
        const cameraDesc = camera.getDescription(state.gltf);
        return cameraDesc;
    }));
    cameraExportChangedObservable.subscribe( cameraDesc => {
        uiModel.copyToClipboard(JSON.stringify(cameraDesc));
    });

    uiModel.captureCanvas.subscribe( () => {
        view.renderFrame(state, canvas.width, canvas.height);
        const dataURL = canvas.toDataURL();

        var element = document.createElement('a');
        element.setAttribute('href', dataURL);
        element.setAttribute('download', "capture.png");

        element.style.display = 'none';
        document.body.appendChild(element);

        element.click();

        document.body.removeChild(element);
    });

    uiModel.camera.pipe(filter(camera => camera === -1)).subscribe( () => {
        state.cameraIndex = undefined;
    });
    uiModel.camera.pipe(filter(camera => camera !== -1)).subscribe( camera => {
        state.cameraIndex = camera;
    });

    uiModel.variant.subscribe( variant => {
        state.variant = variant;
    });

    uiModel.tonemap.subscribe( tonemap => {
        state.renderingParameters.toneMap = tonemap;
    });

    uiModel.debugchannel.subscribe( debugchannel => {
        state.renderingParameters.debugOutput = debugchannel;
    });

    uiModel.skinningEnabled.subscribe( skinningEnabled => {
        state.renderingParameters.skinning = skinningEnabled;
    });

    uiModel.exposurecompensation.subscribe( exposurecompensation => {
        state.renderingParameters.exposure = Math.pow(2, exposurecompensation);
    });

    uiModel.morphingEnabled.subscribe( morphingEnabled => {
        state.renderingParameters.morphing = morphingEnabled;
    });

    uiModel.clearcoatEnabled.subscribe( clearcoatEnabled => {
        state.renderingParameters.clearcoat = clearcoatEnabled;
    });
    uiModel.sheenEnabled.subscribe( sheenEnabled => {
        state.renderingParameters.sheen = sheenEnabled;
    });
    uiModel.transmissionEnabled.subscribe( transmissionEnabled => {
        state.renderingParameters.transmission = transmissionEnabled;
    });

    uiModel.iblEnabled.subscribe( iblEnabled => {
        state.renderingParameters.useIBL = iblEnabled;
    });

    uiModel.renderEnvEnabled.subscribe( renderEnvEnabled => {
        state.renderingParameters.renderEnvironmentMap = renderEnvEnabled;
    });
    uiModel.blurEnvEnabled.subscribe( blurEnvEnabled => {
        state.renderingParameters.blurEnvironmentMap = blurEnvEnabled;
    });

    uiModel.punctualLightsEnabled.subscribe( punctualLightsEnabled => {
        state.renderingParameters.usePunctual = punctualLightsEnabled;
    });

    uiModel.environmentEnabled.subscribe( environmentEnabled => {
        state.renderingParameters.environmentBackground = environmentEnabled;
    });

    uiModel.environmentRotation.subscribe( environmentRotation => {
        switch (environmentRotation)
        {
        case "+Z":
            state.renderingParameters.environmentRotation = 90.0;
            break;
        case "-X":
            state.renderingParameters.environmentRotation = 180.0;
            break;
        case "-Z":
            state.renderingParameters.environmentRotation = 270.0;
            break;
        case "+X":
            state.renderingParameters.environmentRotation = 0.0;
            break;
        }
    });

    uiModel.clearColor.subscribe( clearColor => {
        state.renderingParameters.clearColor = clearColor;
    });

    uiModel.animationPlay.subscribe( animationPlay => {
        if(animationPlay)
        {
            state.animationTimer.unpause();
        }
        else
        {
            state.animationTimer.pause();
        }
    });

    uiModel.activeAnimations.subscribe( animations => {
        state.animationIndices = animations;
    });

    uiModel.hdr.subscribe( hdrFile => {
        resourceLoader.loadEnvironment(hdrFile).then( (environment) => {
            state.environment = environment;
        });
    });

    uiModel.attachGltfLoaded(gltfLoadedMulticast);
    uiModel.updateStatistics(statisticsUpdateObservable);
    const sceneChangedStateObservable = uiModel.scene.pipe(map( newSceneIndex => state));
    uiModel.attachCameraChangeObservable(sceneChangedStateObservable);
    gltfLoadedMulticast.connect();

    const input = new gltfInput(canvas);
    input.setupGlobalInputBindings(document);
    input.setupCanvasInputBindings(canvas);
    input.onRotate = (deltaX, deltaY) =>
    {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.orbit(deltaX, deltaY);
        }
    };
    input.onPan = (deltaX, deltaY) =>
    {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.pan(deltaX, deltaY);
        }
    };
    input.onZoom = (delta) =>
    {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.zoomBy(delta);
        }
    };

    // configure the animation loop
    const update = () =>
    {
        canvas.width = window.innerWidth - ui.getBoundingClientRect().width;
        canvas.height = canvas.clientHeight;

        view.renderFrame(state, canvas.width, canvas.height);
        window.requestAnimationFrame(update);
    };

    // After this start executing animation loop.
    window.requestAnimationFrame(update);
}

export { main };
<|endoftext|>