<commit_before>/**
 * Manager
 */
( function ( root, factory ) {

    if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery',
            './Util',
            './Config'
        ], factory );

    } else if ( typeof exports === 'object' ) {

        module.exports = factory(
            require( 'jquery' ),
            require( './Util' ),
            require( './Config' )
        );

    } else {

        root.AdManager = root.AdManager || {};

        root.AdManager.Manager = factory(
            root.jQuery,
            root.AdManager.Util,
            root.AdManager.Config
        );

    }

} ( this, function ( $, Util, Config ) {

    var name = 'Manager',
        debugEnabled = true,
        debug = debugEnabled ? Util.debug : function () {},
        definedSlots = [],
        pagePositions = [],
        inventory = [],
        account = null,
        defaults = {
            adClass:           'app_ad_unit',      // Outer ad wrap
            adUnitTargetClass: 'app_unit_target',  // Inner ad wrap
            adSelector:        ''                  // Leave blank
        };

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

    function init() {

        if ( ! isEnabled() ) {
            return;
        }

        defaults.adSelector = '.' + defaults.adClass;
        inventory = getInventory();
        account = Config.get( 'account' );
        bindHandlers();
        loadLibrary();

    }

    /**
     * Bind to custom jQuery events
     */
    function bindHandlers() {

        $( document )
            .on( 'GPT:unitsInserted', function ( event ) {
                debug( name + ': GPT:unitsInserted' );
            } )
            .on( 'GPT:libraryLoaded', function ( event ) {
                debug( name + ': GPT:libraryLoaded' );
                initSequence();
            } )
            .on( 'GPT:slotsDefined', function ( event ) {
                debug( name + ': GPT:slotsDefined' );
                displayPageAds();
            } );

    }

    function initSequence() {

        $.event.trigger( 'GPT:initSequence' );

        listenForDfpEvents();
        enableSingleRequest();
        setTargeting();
        setPagePositions();
        defineSlotsForPagePositions();

    }

    /**
     * Get Inventory
     *
     * @return object
     */
    function getInventory() {

        return getAvailableSizes( inventoryCleanTypes( Config.get( 'inventory' ) ) );

    }

    /**
     * Add default unit type if not set
     *
     * @param array inventory
     * @return array inventory
     */
    function inventoryCleanTypes( inventory ) {

        for ( var i = 0; i < inventory.length; i++ ) {

            if ( typeof inventory[ i ].type !== 'undefined' ) {
                continue;
            }

            inventory[ i ].type = 'default';

        }

        return inventory;

    }

    /**
     * Remove sizes from inventory that will not display properly
     *
     * @param array inventory
     * @return array inventory
     */
    function getAvailableSizes( inventory ) {

        var width = ( window.innerWidth > 0 ) ? window.innerWidth : screen.width;

        if ( width > 1024 ) {
            return inventory;
        }

        if ( width >= 768 && width <= 1024 ) {

            var max = 980;

            for ( var i = 0; i < inventory.length; i++ ) {
                var sizesToRemove = [];
                for ( var j = 0; j < inventory[ i ].sizes.length; j++ ) {
                    if ( inventory[ i ].sizes[ j ][0] > max ) {
                        sizesToRemove.push( inventory[ i ].sizes[ j ] );
                    }
                }
                inventory[ i ].sizes = Util.difference( inventory[ i ].sizes, sizesToRemove );
            }
        }

        return inventory;

    }

    /**
     * Request GPT library
     */
    function loadLibrary() {

        var googletag,
            gads,
            useSSL,
            node,
            readyStateLoaded = false
        ;

        window.googletag = window.googletag || {};
        googletag = window.googletag;
        googletag.cmd = googletag.cmd || [];
        gads = document.createElement( 'script' );
        gads.async = true;
        gads.type = 'text/javascript';
        useSSL = 'https:' == document.location.protocol;
        gads.src = ( useSSL ? 'https:' : 'http:' ) + '//www.googletagservices.com/tag/js/gpt.js';
        if ( gads.addEventListener ) {
            gads.addEventListener( 'load', onLibraryLoaded, false );
        } else if ( gads.readyState ) {
            gads.onreadystatechange = function () { // Legacy IE
                if ( ! readyStateLoaded ) {
                    readyStateLoaded = true;
                    onLibraryLoaded();
                }
            };
        }
        node = document.getElementsByTagName( 'script' )[0];
        node.parentNode.insertBefore( gads, node );

    }

    /**
     * Callback when GPT library is loaded
     */
    function onLibraryLoaded() {

        googletag.cmd.push( function () {
            $.event.trigger( 'GPT:libraryLoaded' );
        } );

    }

    /**
     * Bind to GPT events
     */
    function listenForDfpEvents() {

        googletag.cmd.push( function () {
            googletag.pubads()
                .addEventListener( 'slotRenderEnded', function ( event ) {
                    slotRenderEnded( event );
                } )
            ;
        } );

    }

    /**
     * Enable Batched SRA
     *
     * @uses collapseEmptyDivs()
     * @uses enableSingleRequest()
     * @uses disableInitialLoad()
     */
    function enableSingleRequest() {

        googletag.cmd.push( function () {
            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_collapseEmptyDivs
            googletag.pubads().collapseEmptyDivs();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_enableSingleRequest
            googletag.pubads().enableSingleRequest();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_disableInitialLoad
            googletag.pubads().disableInitialLoad();
        } );

    }

    /**
     * Send Targeting
     * Defined in Page Config
     */
    function setTargeting() {

        googletag.cmd.push( function () {

            var targeting = Config.get( 'targeting' );

            if ( ! targeting.length ) {
                return;
            }

            $.each( targeting, function ( key, value ) {
                googletag.pubads().setTargeting( key, value );
            } );

        } );

    }

    /**
     * Set Page Positions
     */
    function setPagePositions() {

        var clientType = Config.get( 'clientType' ),
            $context = $( Config.get( 'context' ) ),
            $units = null,
            selector = defaults.adSelector
        ;

        if ( clientType !== false ) {
            selector += '[data-client-type="' + clientType + '"]';
        }

        $units = $context.find( selector );

        $units.each( function () {
            var id = $( this ).data( 'id' );
            pagePositions.push( id );
        } );

    }

    /**
     * Define Slots for Page Positions
     */
    function defineSlotsForPagePositions() {

        googletag.cmd.push( function () {

            var currentPosition = null,
                $context = $( Config.get( 'context' ) ),
                $unit,
                $unitTarget;

            for ( var i = 0; i < pagePositions.length; i++ ) {

                incrementAdSlot( pagePositions[ i ] );
                currentPosition = getAdInfo( pagePositions[ i ] );

                if ( typeof currentPosition.id == 'undefined' ) {
                    continue;
                }

                // find the empty container div on the page. we
                // will dynamically instantiate the unique ad unit.
                $unit = $context.find( defaults.adSelector + '[data-id="' + currentPosition.id + '"]' );
                $unitTarget = $( '<div />' );

                if ( $unit.length < 1 ) {
                    continue;
                }

                // generate new div
                $unitTarget.addClass( defaults.adUnitTargetClass );
                $unitTarget.attr( 'id', currentPosition.idName );
                $unit.append( $unitTarget );

                // activate
                $unit.addClass( 'active' );

                definedSlots[ i ] = googletag
                    .defineSlot(
                        '/' + account + '/' + currentPosition.slot,
                        currentPosition.sizes,
                        currentPosition.idName
                     )
                    .addService( googletag.pubads() )
                ;

            }

            // Enables GPT services for defined slots
            googletag.enableServices();

            $.event.trigger( 'GPT:slotsDefined' );

        } );

    }

    function displayPageAds() {

        googletag.cmd.push( function () {

            // Fetch and display ads for definedSlots
            googletag.pubads().refresh( definedSlots );

            // lastly, run display code
            for ( var n = 0; n < pagePositions.length; n++ ) {

                currentPosition = getAdInfo( pagePositions[n] );

                if ( $( '#' + currentPosition.idName ).length ) {
                    googletag.display( currentPosition.idName );
                }

            }

        } );

    }

    /**
     * slotRenderEnded - callback after unit is rendered
     *
     * @see https://developers.google.com/doubleclick-gpt/reference
     * @param object unit
     */
    function slotRenderEnded( unit ) {

        var unitName = unit.slot.getAdUnitPath().replace( '/' + account + '/', '' ),
            adInfo = getAdInfo( unitName )
        ;

        $.event.trigger( 'GPT:adUnitRendered', {
            name: unitName,
            id: adInfo.id,
            size: unit.size,
            isEmpty: unit.isEmpty,
            creativeId: unit.creativeId,
            lineItemId: unit.lineItemId,
            serviceName: unit.serviceName
        } );

    }

    /**
     * Increment Ad Slot
     *
     * @param string unit
     */
    function incrementAdSlot( unit ) {

        for ( var i = 0; i < inventory.length; i++ ) {
            if ( inventory[ i ].id !== unit && inventory[ i ].slot !== unit ) {
                continue;
            }

            if ( typeof inventory[ i ].iteration == 'undefined' ) {
                inventory[ i ].iteration = 0;
            }

            // increment
            inventory[ i ].iteration = inventory[ i ].iteration + 1;

            return;
        }

    }

    /**
     * Get Ad Unit Info
     *
     * @param string unit
     * @return object
     */
    function getAdInfo( unit ) {

        var returnObject = {};

        for ( var i = 0; i < inventory.length; i++ ) {
            if ( inventory[ i ].id !== unit && inventory[ i ].slot !== unit ) {
                continue;
            }

            // build return object
            returnObject = inventory[ i ];

            // determine the object's idName
            if ( typeof returnObject.useIterator !== 'undefined' && ! returnObject.useIterator ) {
                // don't use the iterator
                returnObject.idName = returnObject.id;
            } else {
                // use the iterator
                returnObject.idName = returnObject.id + '_' + returnObject.iteration;
            }

            return returnObject;
        }

        return returnObject;

    }

    /**
     * getDefinedSlot
     *
     * @param string name
     * @return object definedSlot
     */
    function getDefinedSlot( name ) {

        var definedSlot = null;

        $.each( definedSlots, function ( i, slot ) {
            var unitName = slot.getAdUnitPath().replace( '/' + account + '/', '' );
            if ( unitName === name ) {
                definedSlot = slot;
                return false;
            }
        } );

        return definedSlot;

    }

    /**
     * displaySlot
     *
     * @param string unit [type or slot]
     */
    function displaySlot( unit ) {

        googletag.cmd.push( function () {
            var position = getAdInfo( unit ),
                slot = getDefinedSlot( position.slot )
            ;
            googletag.pubads().refresh( [ slot ] );
            googletag.display( position.idName );
            removeDefinedSlot( position.slot );
        } );

    }

    /**
     * removeDefinedSlot
     *
     * @param string name
     * @return object definedSlot
     */
    function removeDefinedSlot( name ) {

        $.each( definedSlots, function ( index, slot ) {
            var unitName = slot.getAdUnitPath().replace( '/' + account + '/', '' );
            if ( unitName === name ) {
                definedSlots.remove( index );
            }
        } );

    }

    function getDynamicInventory() {

        var dynamicItems = [],
            type = Config.get( 'clientType' ),
            localContext;

        $.each( inventory, function ( index, position ) {
            if ( ( typeof position.dynamic !== 'undefined' ) && ( position.dynamic === true ) ) {
                if ( ! type || type === position.type ) {
                    dynamicItems.push( position );
                    localContext = position.localContext;
                }
            }
        } );

        return {
            dynamicItems: dynamicItems,
            localContext: localContext
        };

    }

    /**
     * Check if the Ad Manager is enabled for page
     *
     * @return bool
     */
    function isEnabled() {

        return Config.get( 'enabled' );

    }

    /**
     * Empties all ads in a given context
     *
     * @param object options.$context
     * @param bool   options.removeContainer
     */
    function emptyAds( options ) {

        var $context = options.$context,
            removeContainer = options.removeContainer || false;

        $context.find( defaults.adSelector ).empty();

        if ( removeContainer ) {
            $context.find( defaults.adSelector ).remove();
        }

    }

    function getDefaults() {
        return defaults;
    }

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

    return {
        init:                init,
        isEnabled:           isEnabled,
        getDefaults:         getDefaults,
        getAdInfo:           getAdInfo,
        displaySlot:         displaySlot,
        removeDefinedSlot:   removeDefinedSlot,
        getDynamicInventory: getDynamicInventory,
        initSequence:        initSequence,
        emptyAds:            emptyAds
    };

} ) );<commit_msg>Replace `defaults` with Config in Manager<commit_after>/**
 * Manager
 */
( function ( root, factory ) {

    if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery',
            './Util',
            './Config'
        ], factory );

    } else if ( typeof exports === 'object' ) {

        module.exports = factory(
            require( 'jquery' ),
            require( './Util' ),
            require( './Config' )
        );

    } else {

        root.AdManager = root.AdManager || {};

        root.AdManager.Manager = factory(
            root.jQuery,
            root.AdManager.Util,
            root.AdManager.Config
        );

    }

} ( this, function ( $, Util, Config ) {

    var name = 'Manager',
        debugEnabled = true,
        debug = debugEnabled ? Util.debug : function () {},
        definedSlots = [],
        pagePositions = [],
        inventory = [],
        account = null,
        adSelector = '';

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

    function init() {

        if ( ! isEnabled() ) {
            return;
        }

        inventory = Config.get( 'inventory' );
        account = Config.get( 'account' );
        adSelector = Config.get( 'adSelector' );

        bindHandlers();
        loadLibrary();

    }

    /**
     * Bind to custom jQuery events
     */
    function bindHandlers() {

        $( document )
            .on( 'GPT:unitsInserted', function ( event ) {
                debug( name + ': GPT:unitsInserted' );
            } )
            .on( 'GPT:libraryLoaded', function ( event ) {
                debug( name + ': GPT:libraryLoaded' );
                initSequence();
            } )
            .on( 'GPT:slotsDefined', function ( event ) {
                debug( name + ': GPT:slotsDefined' );
                displayPageAds();
            } );

    }

    function initSequence() {

        $.event.trigger( 'GPT:initSequence' );

        listenForDfpEvents();
        enableSingleRequest();
        setTargeting();
        setPagePositions();
        defineSlotsForPagePositions();

    }

    /**
     * Get Inventory
     *
     * @return object
     */
    function getInventory() {

        return getAvailableSizes( inventoryCleanTypes( Config.get( 'inventory' ) ) );

    }

    /**
     * Add default unit type if not set
     *
     * @param array inventory
     * @return array inventory
     */
    function inventoryCleanTypes( inventory ) {

        for ( var i = 0; i < inventory.length; i++ ) {

            if ( typeof inventory[ i ].type !== 'undefined' ) {
                continue;
            }

            inventory[ i ].type = 'default';

        }

        return inventory;

    }

    /**
     * Remove sizes from inventory that will not display properly
     *
     * @param array inventory
     * @return array inventory
     */
    function getAvailableSizes( inventory ) {

        var width = ( window.innerWidth > 0 ) ? window.innerWidth : screen.width;

        if ( width > 1024 ) {
            return inventory;
        }

        if ( width >= 768 && width <= 1024 ) {

            var max = 980;

            for ( var i = 0; i < inventory.length; i++ ) {
                var sizesToRemove = [];
                for ( var j = 0; j < inventory[ i ].sizes.length; j++ ) {
                    if ( inventory[ i ].sizes[ j ][0] > max ) {
                        sizesToRemove.push( inventory[ i ].sizes[ j ] );
                    }
                }
                inventory[ i ].sizes = Util.difference( inventory[ i ].sizes, sizesToRemove );
            }
        }

        return inventory;

    }

    /**
     * Request GPT library
     */
    function loadLibrary() {

        var googletag,
            gads,
            useSSL,
            node,
            readyStateLoaded = false
        ;

        window.googletag = window.googletag || {};
        googletag = window.googletag;
        googletag.cmd = googletag.cmd || [];
        gads = document.createElement( 'script' );
        gads.async = true;
        gads.type = 'text/javascript';
        useSSL = 'https:' == document.location.protocol;
        gads.src = ( useSSL ? 'https:' : 'http:' ) + '//www.googletagservices.com/tag/js/gpt.js';
        if ( gads.addEventListener ) {
            gads.addEventListener( 'load', onLibraryLoaded, false );
        } else if ( gads.readyState ) {
            gads.onreadystatechange = function () { // Legacy IE
                if ( ! readyStateLoaded ) {
                    readyStateLoaded = true;
                    onLibraryLoaded();
                }
            };
        }
        node = document.getElementsByTagName( 'script' )[0];
        node.parentNode.insertBefore( gads, node );

    }

    /**
     * Callback when GPT library is loaded
     */
    function onLibraryLoaded() {

        googletag.cmd.push( function () {
            $.event.trigger( 'GPT:libraryLoaded' );
        } );

    }

    /**
     * Bind to GPT events
     */
    function listenForDfpEvents() {

        googletag.cmd.push( function () {
            googletag.pubads()
                .addEventListener( 'slotRenderEnded', function ( event ) {
                    slotRenderEnded( event );
                } )
            ;
        } );

    }

    /**
     * Enable Batched SRA
     *
     * @uses collapseEmptyDivs()
     * @uses enableSingleRequest()
     * @uses disableInitialLoad()
     */
    function enableSingleRequest() {

        googletag.cmd.push( function () {
            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_collapseEmptyDivs
            googletag.pubads().collapseEmptyDivs();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_enableSingleRequest
            googletag.pubads().enableSingleRequest();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_disableInitialLoad
            googletag.pubads().disableInitialLoad();
        } );

    }

    /**
     * Send Targeting
     * Defined in Page Config
     */
    function setTargeting() {

        googletag.cmd.push( function () {

            var targeting = Config.get( 'targeting' );

            if ( ! targeting.length ) {
                return;
            }

            $.each( targeting, function ( key, value ) {
                googletag.pubads().setTargeting( key, value );
            } );

        } );

    }

    /**
     * Set Page Positions
     */
    function setPagePositions() {

        var clientType = Config.get( 'clientType' ),
            $context = $( Config.get( 'context' ) ),
            $units = null,
            selector = adSelector
        ;

        if ( clientType !== false ) {
            selector += '[data-client-type="' + clientType + '"]';
        }

        $units = $context.find( selector );

        $units.each( function () {
            var id = $( this ).data( 'id' );
            pagePositions.push( id );
        } );

    }

    /**
     * Define Slots for Page Positions
     */
    function defineSlotsForPagePositions() {

        googletag.cmd.push( function () {

            var currentPosition = null,
                $context = $( Config.get( 'context' ) ),
                $unit,
                $unitTarget;

            for ( var i = 0; i < pagePositions.length; i++ ) {

                incrementAdSlot( pagePositions[ i ] );
                currentPosition = getAdInfo( pagePositions[ i ] );

                if ( typeof currentPosition.id == 'undefined' ) {
                    continue;
                }

                // find the empty container div on the page. we
                // will dynamically instantiate the unique ad unit.
                $unit = $context.find( adSelector + '[data-id="' + currentPosition.id + '"]' );
                $unitTarget = $( '<div />' );

                if ( $unit.length < 1 ) {
                    continue;
                }

                // generate new div
                $unitTarget.addClass( Config.get( 'adUnitTargetClass' ) );
                $unitTarget.attr( 'id', currentPosition.idName );
                $unit.append( $unitTarget );

                // activate
                $unit.addClass( 'active' );

                definedSlots[ i ] = googletag
                    .defineSlot(
                        '/' + account + '/' + currentPosition.slot,
                        currentPosition.sizes,
                        currentPosition.idName
                     )
                    .addService( googletag.pubads() )
                ;

            }

            // Enables GPT services for defined slots
            googletag.enableServices();

            $.event.trigger( 'GPT:slotsDefined' );

        } );

    }

    function displayPageAds() {

        googletag.cmd.push( function () {

            // Fetch and display ads for definedSlots
            googletag.pubads().refresh( definedSlots );

            // lastly, run display code
            for ( var n = 0; n < pagePositions.length; n++ ) {

                currentPosition = getAdInfo( pagePositions[n] );

                if ( $( '#' + currentPosition.idName ).length ) {
                    googletag.display( currentPosition.idName );
                }

            }

        } );

    }

    /**
     * slotRenderEnded - callback after unit is rendered
     *
     * @see https://developers.google.com/doubleclick-gpt/reference
     * @param object unit
     */
    function slotRenderEnded( unit ) {

        var unitName = unit.slot.getAdUnitPath().replace( '/' + account + '/', '' ),
            adInfo = getAdInfo( unitName )
        ;

        $.event.trigger( 'GPT:adUnitRendered', {
            name: unitName,
            id: adInfo.id,
            size: unit.size,
            isEmpty: unit.isEmpty,
            creativeId: unit.creativeId,
            lineItemId: unit.lineItemId,
            serviceName: unit.serviceName
        } );

    }

    /**
     * Increment Ad Slot
     *
     * @param string unit
     */
    function incrementAdSlot( unit ) {

        for ( var i = 0; i < inventory.length; i++ ) {
            if ( inventory[ i ].id !== unit && inventory[ i ].slot !== unit ) {
                continue;
            }

            if ( typeof inventory[ i ].iteration == 'undefined' ) {
                inventory[ i ].iteration = 0;
            }

            // increment
            inventory[ i ].iteration = inventory[ i ].iteration + 1;

            return;
        }

    }

    /**
     * Get Ad Unit Info
     *
     * @param string unit
     * @return object
     */
    function getAdInfo( unit ) {

        var returnObject = {};

        for ( var i = 0; i < inventory.length; i++ ) {
            if ( inventory[ i ].id !== unit && inventory[ i ].slot !== unit ) {
                continue;
            }

            // build return object
            returnObject = inventory[ i ];

            // determine the object's idName
            if ( typeof returnObject.useIterator !== 'undefined' && ! returnObject.useIterator ) {
                // don't use the iterator
                returnObject.idName = returnObject.id;
            } else {
                // use the iterator
                returnObject.idName = returnObject.id + '_' + returnObject.iteration;
            }

            return returnObject;
        }

        return returnObject;

    }

    /**
     * getDefinedSlot
     *
     * @param string name
     * @return object definedSlot
     */
    function getDefinedSlot( name ) {

        var definedSlot = null;

        $.each( definedSlots, function ( i, slot ) {
            var unitName = slot.getAdUnitPath().replace( '/' + account + '/', '' );
            if ( unitName === name ) {
                definedSlot = slot;
                return false;
            }
        } );

        return definedSlot;

    }

    /**
     * displaySlot
     *
     * @param string unit [type or slot]
     */
    function displaySlot( unit ) {

        googletag.cmd.push( function () {
            var position = getAdInfo( unit ),
                slot = getDefinedSlot( position.slot )
            ;
            googletag.pubads().refresh( [ slot ] );
            googletag.display( position.idName );
            removeDefinedSlot( position.slot );
        } );

    }

    /**
     * removeDefinedSlot
     *
     * @param string name
     * @return object definedSlot
     */
    function removeDefinedSlot( name ) {

        $.each( definedSlots, function ( index, slot ) {
            var unitName = slot.getAdUnitPath().replace( '/' + account + '/', '' );
            if ( unitName === name ) {
                definedSlots.remove( index );
            }
        } );

    }

    function getDynamicInventory() {

        var dynamicItems = [],
            type = Config.get( 'clientType' ),
            localContext;

        $.each( inventory, function ( index, position ) {
            if ( ( typeof position.dynamic !== 'undefined' ) && ( position.dynamic === true ) ) {
                if ( ! type || type === position.type ) {
                    dynamicItems.push( position );
                    localContext = position.localContext;
                }
            }
        } );

        return {
            dynamicItems: dynamicItems,
            localContext: localContext
        };

    }

    /**
     * Check if the Ad Manager is enabled for page
     *
     * @return bool
     */
    function isEnabled() {

        return Config.get( 'enabled' );

    }

    /**
     * Empties all ads in a given context
     *
     * @param object options.$context
     * @param bool   options.removeContainer
     */
    function emptyAds( options ) {

        var $context = options.$context,
            removeContainer = options.removeContainer || false;

        $context.find( adSelector ).empty();

        if ( removeContainer ) {
            $context.find( adSelector ).remove();
        }

    }

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

    return {
        init:                init,
        isEnabled:           isEnabled,
        getAdInfo:           getAdInfo,
        displaySlot:         displaySlot,
        removeDefinedSlot:   removeDefinedSlot,
        getDynamicInventory: getDynamicInventory,
        initSequence:        initSequence,
        emptyAds:            emptyAds
    };

} ) );<|endoftext|>


<commit_before>//imports

var http = require('http');
var url = require('url');
var crypto = require('crypto');
var uuid = require('node-uuid');
var querystring = require('querystring');
var movieCollection = require('./movie_collection.json');
var movieItem = require('./movie_item.json');
var billboardResponse = require('./billboard.json');
var mongoose = require('mongoose');
var fs = require('fs');
var bunyan = require('bunyan');

//variables
var port = (process.env.PORT || 1337);
var root = '';
var path = '';
var base = 'http://localhost:1337/api/';
var contentType = '';
var responseCj = {};
var responseBody = '';
var responseHeaders = null;
var responseStatus = null;
var requestBody = null;
var log = bunyan.createLogger({name: "api"});

/**
 * Setup DB Connection
 */

// Connect to mongodb
var connect = function () {
    var options = { server: { socketOptions: { keepAlive: 1 } } }
    mongoose.connect('mongodb://localhost/api', options)
}
connect();

// Error handler
mongoose.connection.on('error', function (err) {
    log.info(err)
});

// Reconnect when closed
mongoose.connection.on('disconnected', function () {
    connect()
});

// On Connection
mongoose.connection.once('open', function callback () {
    log.info('mongod connected successfully');
});

// Bootstrap models
fs.readdirSync('../models').forEach(function (file) {
    if (~file.indexOf('.js')) {
      require('../models/' + file);
    }
});

/**
 * Lets Run Some Tests with Mongoose
 */

// create and save movie
var instance = new mongoose.models.Movie({ name: 'Red Dawn'});
log.info('instance.name: ' + instance.name);

/*
instance.save(function (err, instance) {
    if (err) {
        return console.error(err);
    }
    console.log('instance.name: ' + instance.name + ' saved');
});
*/

// lets run a query
mongoose.models.Movie.find(function (err, movies) {
    if (err) {
        return console.error(err);
    }

    if (movies && movies.length > 0) {
        for (var i = 0; i < movies.length; i++) {
            log.info({movie: movies[i]});
        }
    }
});

/**
 * Route Patterns
 */
var reAPIBillboard = new RegExp('^\/api\/$','i');
var reAPIListMovies = new RegExp('^\/api\/movies$','i');
var reAPIItemMovies = new RegExp('^\/api\/movies\/.*','i');

/**
 * Handle incoming requests
 * @param req
 * @param res
 */
var handler = function (req, res) {

    //some simple content negotiation
    if (req.headers.accept && req.headers.accept.indexOf('application/vnd.collection+json') != -1) {
        contentType = 'application/vnd.collection+json';
    } else {
        contentType = 'application/json';
    }

    var flg = false;
    root = 'http://' + req.headers.host;
    path = url.parse(req.url).pathname;

    //capture request body
    requestBody = '';
    req.on('data', function (chunk) {
        requestBody += chunk;
    });

    //billboard route - RegExp('^\/api\/$','i')
    if (reAPIBillboard.test(req.url)) {
        flg = true;
        console.log('reAPIBillboard true');

        switch(req.method) {
            case 'GET':
                sendBillboardResponse(req, res);
                break;
            default:
                break;
        }
    }

    //movies collection route - RegExp('^\/api\/movies$','i')
    if (flg === false && reAPIListMovies.test(req.url)) {
        flg = true;
        console.log('reAPIListMovies true');

        switch(req.method) {
            case 'GET':
                sendListResponseMovies(req, res);
                break;
            case 'POST':
                sendAddMovieResponse(req, res);
                break;
            case 'DELETE':
                sendDeleteResponse(req, res);
                break;
            default:
                break;
        }
    }

    //movie item route - RegExp('^\/api\/movies\/.*','i')
    if (flg === false && reAPIItemMovies.test(req.url)) {
        flg = true;
        console.log('reAPIItemMovies true');

        switch(req.method) {
            case 'GET':
                sendItemResponseMovies(req, res);
                break;
            case 'POST':
                //sendNotAcceptedResponse
            case 'PUT':
                //sendItemUpdateResponseMovies(req, res);
                break;
            case 'DELETE':
                sendDeleteResponse(req, res);
                break;
            default:
                break;
        }
    }

    //not found
    if (flg === false) {
        console.log('send not found');
        sendNotFoundResponse(req, res);
    }
};

var sendAddMovieResponse = function (req, res) {
    req.on('end', function () {

        //TODO: add validation and add functionality

        //build response
        var id = uuid();
        id = id.replace(/-/g,'');

        //responseCj = billboardResponse;
        responseBody = '';
        responseHeaders = {
            'Location': base + 'movies/' + id
        }
        responseStatus = 201;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendBillboardResponse = function (req, res) {
    req.on('end', function () {
        //build response

        responseCj = billboardResponse;
        responseBody = JSON.stringify(responseCj);
        responseHeaders = {
            'Content-Type': contentType,
            'Content-Length': Buffer.byteLength(responseBody)
        }
        responseStatus = 200;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendDeleteResponse = function (req, res) {
    responseBody = '';
    responseHeaders = {};
    responseStatus = 204;
    sendResponse(req, res, responseStatus, responseHeaders, responseBody);
};

var sendNotFoundResponse = function (req, res) {
    responseBody = '';
    responseHeaders = {};
    responseStatus = 404;
    sendResponse(req, res, responseStatus, responseHeaders, responseBody);
};

var sendListResponseMovies = function (req, res) {
    req.on('end', function () {
        //build response

        responseCj = movieCollection;
        responseBody = JSON.stringify(responseCj);
        responseHeaders = {
            'Content-Type': contentType,
            'Content-Length': Buffer.byteLength(responseBody)
        }
        responseStatus = 200;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendItemResponseMovies = function (req, res) {
    req.on('end', function () {
        //build response

        responseCj = movieItem;
        responseBody = JSON.stringify(responseCj);
        responseHeaders = {
            'Content-Type': contentType,
            'Content-Length': Buffer.byteLength(responseBody)
        }
        responseStatus = 200;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendResponse = function (req, res, responseStatus, responseHeaders, responseBody) {
    res.writeHead(responseStatus, responseHeaders);
    res.end(responseBody);
};

//main
http.createServer(handler).listen(port);

var cjTemplate = {
    "collection":
    {
        "version":"0.1",
        "href": base,
        "links":[
            base + "/movies",
            base + "/actors"
        ],
        "items":[],
        "queries":[],
        "template":{},
        "error":{}
    }
};

/*
 var server = http.createServer(function(request, response) {
 var body = '';

 request.on('data', function (chunk) {
 body += chunk;
 });

 request.on('end', function () {
 var now = new Date();
 var msg = '';

 console.log('request received: ' + now.toISOString());
 console.log('request.method: ' + request.method);
 console.log('request.url: ' + request.url);
 console.log('request.httpVersion: ' + request.httpVersion);
 console.log('request.headers:');

 //log headers
 for (var header in request.headers) {
 console.log('  ' + header + ': ' + request.headers[header]);
 }

 console.log('request.body:');
 //console.log('  ' + body);

 try {
 msg = JSON.parse(body);
 console.log(JSON.stringify(msg, null, 4)); //4 is the spacer arg
 }
 catch(ex) {
 console.log('error parsing body as JSON');
 }

 console.log(); //insert line break
 response.writeHead(200);
 response.end();
 });
 }).listen(port, 'localhost');
 console.log('server listening at http://localhost:8080');
 */

/**
 * Sample Requests
 */
//curl -X POST -v -H "Content-Type: application/vnd.collection+json" -H "Connection: close" -H "Cache-Control: no-cache" -d '{"template":{"data":[{"name":"text","value":"testing"},{"name":"junk","value":""}]}}' http://localhost:8080/api/
<commit_msg>Add Collection response buildout<commit_after>//imports

var http = require('http');
var url = require('url');
var crypto = require('crypto');
var uuid = require('node-uuid');
var querystring = require('querystring');
var movieCollectionStatic = require('./movie_collection.json');
var movieItemStatic = require('./movie_item.json');
var billboardResponseStatic = require('./billboard.json');
var mongoose = require('mongoose');
var fs = require('fs');
var bunyan = require('bunyan');

//variables
var port = (process.env.PORT || 1337);
var root = '';
var path = '';
var base = '/';
var contentType = '';
var responseCj = {};
var responseBody = '';
var responseHeaders = null;
var responseStatus = null;
var requestBody = null;
var log = bunyan.createLogger({name: "api"});

/**
 * Setup DB Connection
 */

// Connect to mongodb
var connect = function () {
    var options = { server: { socketOptions: { keepAlive: 1 } } }
    mongoose.connect('mongodb://localhost/api', options)
}
connect();

// Error handler
mongoose.connection.on('error', function (err) {
    log.info(err)
});

// Reconnect when closed
mongoose.connection.on('disconnected', function () {
    connect()
});

// On Connection
mongoose.connection.once('open', function callback () {
    log.info('mongod connected successfully');
});

// Bootstrap models
fs.readdirSync('../models').forEach(function (file) {
    if (~file.indexOf('.js')) {
      require('../models/' + file);
    }
});

/**
 * Lets Run Some Tests with Mongoose
 */

// create and save movie
var instance = new mongoose.models.Movie({ name: 'Red Dawn'});
log.info('instance.name: ' + instance.name);

/*
instance.save(function (err, instance) {
    if (err) {
        return console.error(err);
    }
    console.log('instance.name: ' + instance.name + ' saved');
});
*/

// lets run a query
mongoose.models.Movie.find(function (err, movies) {
    if (err) {
        return console.error(err);
    }

    if (movies && movies.length > 0) {
        for (var i = 0; i < movies.length; i++) {
            log.info({movie: movies[i]});
        }
    }
});

/**
 * Route Patterns
 */
var reAPIBillboard = new RegExp('^\/api\/$','i');
var reAPIListMovies = new RegExp('^\/api\/movies$','i');
var reAPIItemMovies = new RegExp('^\/api\/movies\/.*','i');

/**
 * Create Collection JSON Response Template
 */
var createResponseCjTemplate = function() {
    responseCj.collection = {};
    responseCj.version = "1.0";
    responseCj.href = base + path;

    responseCj.links = [];
    responseCj.links.push({'rel': 'home', 'href': base});

    responseCj.items = [];
    responseCj.queries = [];
    responseCj.template = {};
};

/**
 * Handle incoming requests
 * @param req
 * @param res
 */
var handler = function (req, res) {
    //some simple content negotiation
    if (req.headers.accept && req.headers.accept.indexOf('application/vnd.collection+json') != -1) {
        contentType = 'application/vnd.collection+json';
    } else {
        contentType = 'application/json';
    }

    var flg = false;
    root = 'http://' + req.headers.host;
    base = 'http://' + req.headers.host;
    path = url.parse(req.url).pathname;

    //capture request body
    requestBody = '';
    req.on('data', function (chunk) {
        requestBody += chunk;
    });

    //billboard route - RegExp('^\/api\/$','i')
    if (reAPIBillboard.test(req.url)) {
        flg = true;
        console.log('reAPIBillboard true');

        switch(req.method) {
            case 'GET':
                sendBillboardResponse(req, res);
                break;
            default:
                break;
        }
    }

    //movies collection route - RegExp('^\/api\/movies$','i')
    if (flg === false && reAPIListMovies.test(req.url)) {
        flg = true;
        console.log('reAPIListMovies true');

        switch(req.method) {
            case 'GET':
                sendListResponseMovies(req, res);
                break;
            case 'POST':
                sendAddMovieResponse(req, res);
                break;
            case 'DELETE':
                sendDeleteResponse(req, res);
                break;
            default:
                break;
        }
    }

    //movie item route - RegExp('^\/api\/movies\/.*','i')
    if (flg === false && reAPIItemMovies.test(req.url)) {
        flg = true;
        console.log('reAPIItemMovies true');

        switch(req.method) {
            case 'GET':
                sendItemResponseMovies(req, res);
                break;
            case 'POST':
                //sendNotAcceptedResponse
            case 'PUT':
                //sendItemUpdateResponseMovies(req, res);
                break;
            case 'DELETE':
                sendDeleteResponse(req, res);
                break;
            default:
                break;
        }
    }

    //not found
    if (flg === false) {
        console.log('send not found');
        sendNotFoundResponse(req, res);
    }
};

var sendAddMovieResponse = function (req, res) {
    req.on('end', function () {

        //TODO: add validation and add functionality

        //build response
        var id = uuid();
        id = id.replace(/-/g,'');

        //responseCj = billboardResponseStatic;
        responseBody = '';
        responseHeaders = {
            'Location': base + 'movies/' + id
        }
        responseStatus = 201;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendBillboardResponse = function (req, res) {
    req.on('end', function () {
        //build response

        responseCj = billboardResponseStatic;
        responseBody = JSON.stringify(responseCj);
        responseHeaders = {
            'Content-Type': contentType,
            'Content-Length': Buffer.byteLength(responseBody)
        }
        responseStatus = 200;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendDeleteResponse = function (req, res) {
    responseBody = '';
    responseHeaders = {};
    responseStatus = 204;
    sendResponse(req, res, responseStatus, responseHeaders, responseBody);
};

var sendNotFoundResponse = function (req, res) {
    responseBody = '';
    responseHeaders = {};
    responseStatus = 404;
    sendResponse(req, res, responseStatus, responseHeaders, responseBody);
};

/**
 * Render Movie Write Template (POST, PUT)
 */
var renderMovieCollectionTemplate = function () {
    var template = {};
    var item = {};

    template.data = [];

    item = {};
    item.name = 'name';
    item.value = '';
    item.prompt = 'title of movie';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'description';
    item.value = '';
    item.prompt = 'description of movie';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'datePublished';
    item.value = '';
    item.prompt = 'date movie was published';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'about';
    item.value = '';
    item.prompt = 'short description of film';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'genre';
    item.value = '';
    item.prompt = 'movie genre';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'version';
    item.value = '';
    item.prompt = 'version of thie release';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'timeRequired';
    item.value = '';
    item.prompt = 'time required to view this movie aka duration';
    item.required = 'true';
    template.data.push(item);

    item = {};
    item.name = 'contentRating';
    item.value = '';
    item.prompt = 'movie content rating';
    item.required = 'true';
    template.data.push(item);

    responseCj.collection.template = template;
};

/**
 * Render movie collection queries
 */
var renderMovieCollectionQueries = function () {
    var query = {};

    query = {};
    query.rel = 'collection sort';
    query.prompt = 'Sort by Name';
    query.href = base + '/sortbyname';
    responseCj.queries.push(query);

};

/**
 * Render movie collection items
 */
var renderMovieCollectionItems = function (coll) {

    var item = {};
    var dataItem = {};

    id = '613856331910938';
    item.href = base + '/' + id;
    item.data = [];
    item.links = [];

    dataItem = {};
    dataItem.name = 'description';
    dataItem.value = 'An exclusive golf course has to deal with a brash new member and a destructive dancing gopher.';
    dataItem.prompt = 'description of movie';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'datePublished'
    dataItem.value = '2013-03-28T21:51:08.406Z';
    dataItem.prompt = 'date movie was published';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'name'
    dataItem.value = 'Caddyshack';
    dataItem.prompt = 'title of the movie';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'about'
    dataItem.value = 'An exclusive golf course has to deal with a brash new member and a destructive dancing gopher.';
    dataItem.prompt = 'short description of this item';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'genre';
    dataItem.value = 'comedy';
    dataItem.prompt = 'movie genre';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'version';
    dataItem.value = '1';
    dataItem.prompt = 'version of this release';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'timeRequired';
    dataItem.value = 'PT180M';
    dataItem.prompt = 'time required to view this movie, aka duration';
    item.data.push(dataItem);

    dataItem = {};
    dataItem.name = 'contentRating';
    dataItem.value = 'R';
    dataItem.prompt = 'rating of the movie';
    item.data.push(dataItem);

    linkItem = {};
    linkItem.name = "harold ramis";
    linkItem.rel = 'directory';
    linkItem.prompt = 'director of the movie';
    linkItem.href = base + path + '/' + id;
    linkItem.render = 'link';
    item.links.push(linkItem);

    responseCj.collection.items.push(item);

};

var sendListResponseMovies = function (req, res) {
    req.on('end', function () {

        var coll = []; //fix this

        //build response
        createResponseCjTemplate();
        renderMovieCollectionTemplate();
        renderMovieCollectionQueries();
        renderMovieCollectionItems(coll);

        responseBody = JSON.stringify(responseCj);
        responseHeaders = {
            'Content-Type': contentType,
            'Content-Length': Buffer.byteLength(responseBody)
        }
        responseStatus = 200;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendItemResponseMovies = function (req, res) {
    req.on('end', function () {
        //build response

        responseCj = movieItemStatic;
        responseBody = JSON.stringify(responseCj);
        responseHeaders = {
            'Content-Type': contentType,
            'Content-Length': Buffer.byteLength(responseBody)
        }
        responseStatus = 200;

        sendResponse(req, res, responseStatus, responseHeaders, responseBody);
    });
};

var sendResponse = function (req, res, responseStatus, responseHeaders, responseBody) {
    res.writeHead(responseStatus, responseHeaders);
    res.end(responseBody);
};

//main
http.createServer(handler).listen(port);

var cjTemplate = {
    "collection":
    {
        "version":"0.1",
        "href": base,
        "links":[
            base + "/movies",
            base + "/actors"
        ],
        "items":[],
        "queries":[],
        "template":{},
        "error":{}
    }
};

/*
 var server = http.createServer(function(request, response) {
 var body = '';

 request.on('data', function (chunk) {
 body += chunk;
 });

 request.on('end', function () {
 var now = new Date();
 var msg = '';

 console.log('request received: ' + now.toISOString());
 console.log('request.method: ' + request.method);
 console.log('request.url: ' + request.url);
 console.log('request.httpVersion: ' + request.httpVersion);
 console.log('request.headers:');

 //log headers
 for (var header in request.headers) {
 console.log('  ' + header + ': ' + request.headers[header]);
 }

 console.log('request.body:');
 //console.log('  ' + body);

 try {
 msg = JSON.parse(body);
 console.log(JSON.stringify(msg, null, 4)); //4 is the spacer arg
 }
 catch(ex) {
 console.log('error parsing body as JSON');
 }

 console.log(); //insert line break
 response.writeHead(200);
 response.end();
 });
 }).listen(port, 'localhost');
 console.log('server listening at http://localhost:8080');
 */

/**
 * Sample Requests
 */
//curl -X POST -v -H "Content-Type: application/vnd.collection+json" -H "Connection: close" -H "Cache-Control: no-cache" -d '{"template":{"data":[{"name":"text","value":"testing"},{"name":"junk","value":""}]}}' http://localhost:8080/api/
<|endoftext|>


<commit_before>var userList;

jQuery(function(){
	IO = new IO();

	userList = new UserList();
	uli = new UserListUI(userList, $('#user_list .users'), $('#user_list .top'));

	UI.emit('init');

	[
		UI.input.nameCompletion,
		UI.input
	].
	forEach(function(watcher){
		watcher.subscribeToEvents();
	});

	IO.addMessageHandler(UI.Messages.list);
	IO.addMessageHandler(UI.Messages);
	IO.addMessageHandler(userList);

	IO.connect();

	document.getElementById("message").focus();
});
<commit_msg>DRY up client side main.js a little<commit_after>var userList;

jQuery(function(){
	IO = new IO();

	userList = new UserList();
	uli = new UserListUI(userList, $('#user_list .users'), $('#user_list .top'));

	UI.emit('init');

	[
		UI.input.nameCompletion,
		UI.input
	].
	forEach(function(watcher){
		watcher.subscribeToEvents();
	});

	[
		UI.Messages.list,
		UI.Messages,
		userList
	].forEach(IO.addMessageHandler)

	IO.connect();

	document.getElementById("message").focus();
});
<|endoftext|>


<commit_before>// To support using nodejs modules that do module.exports = ...
var module = {};

var Util = {
Text: (function Text(){
	var my_name = (window.Me && Me.name && Me.name.toLowerCase()) || '';
	var filter = (function(){
		var f = window.UiOptions && UiOptions.wordFilter;
		return f && new RegExp(f.join('|'), 'gi');
	})();

	return {
		newlineMatcher: /\n|\r\n/gim,

      linkify: function(text) {
         var lines = text.split("\n");
         text = "";
         for (i = 0; i < lines.length; ++i) {
            var words = lines[i].split(" ");
            lines[i] = "";
            for (j = 0; j < words.length; ++j) {
               var tmp = words[j];
               words[j] = this.parseYoutube(words[j]);

               if (words[j] == tmp) {
                  words[j] = this.parseImg(words[j]);

                  if (words[j] == tmp) {
                     words[j] = this.parseLink(words[j]);
                  }
               }

               lines[i] += words[j] + (j != words.length - 1 ? " " : "");
            }
            text += lines[i] + (i != lines.length - 1 ? "\n" : "");
         }

         return text;
      },

      parseImg: function(text) {
         var url = "";
         if (this.matchHttp(text)) {
            url = text;
         } else if (this.matchWWW(text)) {
            url = "http://" + text;
         }

         if (url != "" && this.matchImgSuffix(url)) {
            return '';
         }

         return text;
      },

      parseYoutube: function(text) {
         var id = this.getYoutubeId(text);
         var rr = id == "oHg5SJYRHA0" ? 1 : 0;
         var url = 'https://www.youtube.com/v/' + id + '?version=3&autoplay=' + rr;
         if ((this.matchHttp(text) || this.matchWWW(text)) && this.matchYoutube(text)) {
               return '<object width="640" height="390"><param name="movie" value="' + url + '"></param><param name="allowScriptAccess" value="always"></param><embed src="' + url + '" type="application/x-shockwave-flash" allowscriptaccess="always" width="640" height="390"></embed></object>'
         }

         return text;
      },

      parseLink: function(text) {
         if (this.matchHttp(text)) {
            return '' + text + '';
         } else if (this.matchWWW(text)) {
            var url = "http://" + text;
            return '' + text + '';
         } else {
            return text;
         }
      },

		linkifyTest: function(text){
			return this.matchHttp(text) ||
			       this.matchWWW(text) ||
			       this.matchMailto(text);
		},

      // We need these four match methods because regex.test() is buggy when
      // you reuse a regex object. Other regex functions are fine though...
      matchHttp: function(text) {
         return (/(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim).test(text);
      },

      matchWWW: function(text) {
         return (/(^|[^\/])(www\.[\S]+(\b|$))/gim).test(text);
      },

      matchMailto: function(text) {
         return (/(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim).test(text);
      },

      matchYoutube: function(text) {
         return (/youtube\.com|youtu\.be/).test(text);
      },

      getYoutubeId: function(text) {
         var id = (/[a-zA-Z0-9\-\_]{11}/).exec((/(v=|\/v\/|\/embed\/)[a-zA-Z0-9\-\_]{11}/).exec(text));
         return id;
      },

      matchImgSuffix: function(text) {
         return (/\.(gif|jpg|jpeg|png|tiff|tif|svg|mini|thumbnail|standard|medium|large|huge)/gim).test(text);
      },

		mentionMatcher: function(text){
			var matches = text.match(/@([^\s]+)/g);
			return matches && matches.some(function(match){
				match = match.substr(1).toLowerCase();
            // does it match @<name> or @all or @everyone or @everybody?
            var matches_name_or_all =
             my_name.indexOf(match) >= 0 ||
             match == "all" ||
             match == "everybody" ||
             match == "everyone";
				return match.length >= 2 && matches_name_or_all;
			});
		},

      imageDisplay: function(text){
         if (this.matchHttp(text) || this.matchWWW(text)) {
            if (this.matchWWW(text))
               text = 'http://' + text;

            if (this.imageSuffixMatch(text)) {
               text = text.replace(http_matcher, ' ');
            }
         }
         return text;
      },

		wordFilter: function(text){
			if(!filter) return text;
			return text.replace(filter, function(m) {
				return Array(m.length+1).join("*");
			});
		},
	};
})(),

nextTick: function(f){
	return setTimeout(f,0);
},

ts:function(){
	return new Date().getTime();
},

time:function time(t){
	var H = t.getHours(),
			h = H % 12,
			m = t.getMinutes();

	return (h==0 ? 12 : h) + ":" + (m < 10 ? '0' + m : m) + (H > 11 ? 'pm' : 'am');
},

date:function date(t){
	var d = t.getDate(),
	    m = t.getMonth() + 1;

	return m + "/" + (d < 10 ? '0' + d : d);
},

url:function(){
	return window.location.href.split('?')[0];
},

inherits:function(to, from){
	$.extend(to.prototype, from.prototype, {_super: from});
}
};

if(window.EventEmitter){
	EventEmitter.prototype.muteEvents = function(fn){
		this.emit = function(){}
		fn();
		delete this.emit;
	}
}
<commit_msg>Restricted images to thumbnail 300px width/height<commit_after>// To support using nodejs modules that do module.exports = ...
var module = {};

var Util = {
Text: (function Text(){
	var my_name = (window.Me && Me.name && Me.name.toLowerCase()) || '';
	var filter = (function(){
		var f = window.UiOptions && UiOptions.wordFilter;
		return f && new RegExp(f.join('|'), 'gi');
	})();

	return {
		newlineMatcher: /\n|\r\n/gim,

      linkify: function(text) {
         var lines = text.split("\n");
         text = "";
         for (i = 0; i < lines.length; ++i) {
            var words = lines[i].split(" ");
            lines[i] = "";
            for (j = 0; j < words.length; ++j) {
               var tmp = words[j];
               words[j] = this.parseYoutube(words[j]);

               if (words[j] == tmp) {
                  words[j] = this.parseImg(words[j]);

                  if (words[j] == tmp) {
                     words[j] = this.parseLink(words[j]);
                  }
               }

               lines[i] += words[j] + (j != words.length - 1 ? " " : "");
            }
            text += lines[i] + (i != lines.length - 1 ? "\n" : "");
         }

         return text;
      },

      parseImg: function(text) {
         var url = "";
         if (this.matchHttp(text)) {
            url = text;
         } else if (this.matchWWW(text)) {
            url = "http://" + text;
         }

         if (url != "" && this.matchImgSuffix(url)) {
            return '';
         }

         return text;
      },

      parseYoutube: function(text) {
         var id = this.getYoutubeId(text);
         var rr = id == "oHg5SJYRHA0" ? 1 : 0;
         var url = 'https://www.youtube.com/v/' + id + '?version=3&autoplay=' + rr;
         if ((this.matchHttp(text) || this.matchWWW(text)) && this.matchYoutube(text)) {
               return '<object width="640" height="390"><param name="movie" value="' + url + '"></param><param name="allowScriptAccess" value="always"></param><embed src="' + url + '" type="application/x-shockwave-flash" allowscriptaccess="always" width="640" height="390"></embed></object>'
         }

         return text;
      },

      parseLink: function(text) {
         if (this.matchHttp(text)) {
            return '' + text + '';
         } else if (this.matchWWW(text)) {
            var url = "http://" + text;
            return '' + text + '';
         } else {
            return text;
         }
      },

		linkifyTest: function(text){
			return this.matchHttp(text) ||
			       this.matchWWW(text) ||
			       this.matchMailto(text);
		},

      // We need these four match methods because regex.test() is buggy when
      // you reuse a regex object. Other regex functions are fine though...
      matchHttp: function(text) {
         return (/(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim).test(text);
      },

      matchWWW: function(text) {
         return (/(^|[^\/])(www\.[\S]+(\b|$))/gim).test(text);
      },

      matchMailto: function(text) {
         return (/(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim).test(text);
      },

      matchYoutube: function(text) {
         return (/youtube\.com|youtu\.be/).test(text);
      },

      getYoutubeId: function(text) {
         var id = (/[a-zA-Z0-9\-\_]{11}/).exec((/(v=|\/v\/|\/embed\/)[a-zA-Z0-9\-\_]{11}/).exec(text));
         return id;
      },

      matchImgSuffix: function(text) {
         return (/\.(gif|jpg|jpeg|png|tiff|tif|svg|mini|thumbnail|standard|medium|large|huge)/gim).test(text);
      },

		mentionMatcher: function(text){
			var matches = text.match(/@([^\s]+)/g);
			return matches && matches.some(function(match){
				match = match.substr(1).toLowerCase();
            // does it match @<name> or @all or @everyone or @everybody?
            var matches_name_or_all =
             my_name.indexOf(match) >= 0 ||
             match == "all" ||
             match == "everybody" ||
             match == "everyone";
				return match.length >= 2 && matches_name_or_all;
			});
		},

      imageDisplay: function(text){
         if (this.matchHttp(text) || this.matchWWW(text)) {
            if (this.matchWWW(text))
               text = 'http://' + text;

            if (this.imageSuffixMatch(text)) {
               text = text.replace(http_matcher, ' ');
            }
         }
         return text;
      },

		wordFilter: function(text){
			if(!filter) return text;
			return text.replace(filter, function(m) {
				return Array(m.length+1).join("*");
			});
		},
	};
})(),

nextTick: function(f){
	return setTimeout(f,0);
},

ts:function(){
	return new Date().getTime();
},

time:function time(t){
	var H = t.getHours(),
			h = H % 12,
			m = t.getMinutes();

	return (h==0 ? 12 : h) + ":" + (m < 10 ? '0' + m : m) + (H > 11 ? 'pm' : 'am');
},

date:function date(t){
	var d = t.getDate(),
	    m = t.getMonth() + 1;

	return m + "/" + (d < 10 ? '0' + d : d);
},

url:function(){
	return window.location.href.split('?')[0];
},

inherits:function(to, from){
	$.extend(to.prototype, from.prototype, {_super: from});
}
};

if(window.EventEmitter){
	EventEmitter.prototype.muteEvents = function(fn){
		this.emit = function(){}
		fn();
		delete this.emit;
	}
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as MenuBarActions from './actions.js';
import {DEFAULT, LIKE, TIME} from './fetcher';
import { WithContext as ReactTags } from 'react-tag-input';

class MenuBar extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {menuOpen: false};
  };
  toggleMenu() {
    this.setState({menuOpen: !this.state.menuOpen});
  }
  render(){
    const {sorting, loadImages, sortingChanged, tags, selectedTags, addTag, removeTag} = this.props;
    const {menuOpen} = this.state;
    function radioSelected(selected){
        sortingChanged(selected);
        loadImages();
    }

    function handleDelete(index, tag){
      let indexOfLastTag  = (selectedTags.length - 1);
      removeTag(indexOfLastTag, selectedTags[indexOfLastTag])
      loadImages();
    }

    function handleAdd(tag){
      addTag(tag)
      loadImages();
    }

    let placeholder = "tagger"
    let suggestions = Object.keys(tags);
    let menubarContentClass = "menubar-content";
    if (!menuOpen) menubarContentClass += " open";
    return (

              <form className="sort-buttons">
                <text>Popularitet</text><input type="radio" name="sorting" checked={sorting == DEFAULT} onChange={() => radioSelected(DEFAULT)}/>
                <text>Likes</text><input type="radio" name="sorting" checked={sorting == LIKE} onChange={() => radioSelected(LIKE)}/>
                <text>Tid</text><input type="radio" name="sorting" checked={sorting == TIME} onChange={() => radioSelected(TIME)}/>
              </form>

              <SeletedTags selectedTags={selectedTags} addTag={handleAdd} removeTag={removeTag} loadImages={loadImages}></SeletedTags>
              <ReactTags
                suggestions={suggestions}
                labelField={'name'}
                placeholder={placeholder}
                handleAddition={handleAdd}
                handleDelete={handleDelete}
                autocomplete={true}
                minQueryLength={2}
                allowDeleteFromEmptyInput={true}/>

);
  }
};

class SeletedTags extends Component {
  render(){
    function handleDelete(index, tag){
      removeTag(index, tag);
      loadImages();
    }
    const {selectedTags, addTag, removeTag, loadImages} =  this.props;
    return (

      {selectedTags.map((tag, index) => {return (
          <button key={index} type="button" className="selectedTagDeleteButton" onClick={handleDelete.bind(this, index, tag)}> #{tag}</button> )})}

)
  }
}

function mapStateToProps(state){
  return {
    sorting: state.sorting,
    tags: state.tags,
    selectedTags: state.selectedTags

  }
}

function mapActionCreatorsToProps(dispatch){
  return bindActionCreators(MenuBarActions, dispatch);
}

export default connect(mapStateToProps, mapActionCreatorsToProps)(MenuBar);<commit_msg>hashtag istede for tagger<commit_after>import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as MenuBarActions from './actions.js';
import {DEFAULT, LIKE, TIME} from './fetcher';
import { WithContext as ReactTags } from 'react-tag-input';

class MenuBar extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {menuOpen: false};
  };
  toggleMenu() {
    this.setState({menuOpen: !this.state.menuOpen});
  }
  render(){
    const {sorting, loadImages, sortingChanged, tags, selectedTags, addTag, removeTag} = this.props;
    const {menuOpen} = this.state;
    function radioSelected(selected){
        sortingChanged(selected);
        loadImages();
    }

    function handleDelete(index, tag){
      let indexOfLastTag  = (selectedTags.length - 1);
      removeTag(indexOfLastTag, selectedTags[indexOfLastTag])
      loadImages();
    }

    function handleAdd(tag){
      addTag(tag)
      loadImages();
    }

    let placeholder = "#"
    let suggestions = Object.keys(tags);
    let menubarContentClass = "menubar-content";
    if (!menuOpen) menubarContentClass += " open";
    return (

              <form className="sort-buttons">
                <text>Popularitet</text><input type="radio" name="sorting" checked={sorting == DEFAULT} onChange={() => radioSelected(DEFAULT)}/>
                <text>Likes</text><input type="radio" name="sorting" checked={sorting == LIKE} onChange={() => radioSelected(LIKE)}/>
                <text>Tid</text><input type="radio" name="sorting" checked={sorting == TIME} onChange={() => radioSelected(TIME)}/>
              </form>

              <SeletedTags selectedTags={selectedTags} addTag={handleAdd} removeTag={removeTag} loadImages={loadImages}></SeletedTags>
              <ReactTags
                suggestions={suggestions}
                labelField={'name'}
                placeholder={placeholder}
                handleAddition={handleAdd}
                handleDelete={handleDelete}
                autocomplete={true}
                minQueryLength={2}
                allowDeleteFromEmptyInput={true}/>

);
  }
};

class SeletedTags extends Component {
  render(){
    function handleDelete(index, tag){
      removeTag(index, tag);
      loadImages();
    }
    const {selectedTags, addTag, removeTag, loadImages} =  this.props;
    return (

      {selectedTags.map((tag, index) => {return (
          <button key={index} type="button" className="selectedTagDeleteButton" onClick={handleDelete.bind(this, index, tag)}> #{tag}</button> )})}

)
  }
}

function mapStateToProps(state){
  return {
    sorting: state.sorting,
    tags: state.tags,
    selectedTags: state.selectedTags

  }
}

function mapActionCreatorsToProps(dispatch){
  return bindActionCreators(MenuBarActions, dispatch);
}

export default connect(mapStateToProps, mapActionCreatorsToProps)(MenuBar);<|endoftext|>


<commit_before>module.exports.whenEntityLoaded = function(entity, callback) {
  if (entity.hasLoaded) { callback(); }
  entity.addEventListener('loaded', function () {
    callback();
  });
}

module.exports.createHtmlNodeFromString = function(str) {
  var div = document.createElement('div');
  div.innerHTML = str;
  var child = div.firstChild;
  return child;
}

module.exports.getNetworkOwner = function(entity) {
  var components = entity.components;
  if (components.hasOwnProperty('networked-remote')) {
    return entity.components['networked-remote'].data.owner;
  } else if (components.hasOwnProperty('networked')) {
    return entity.components['networked'].owner;
  }
  return null;
}

module.exports.getNetworkId = function(entity) {
  var components = entity.components;
  if (components.hasOwnProperty('networked-remote')) {
    return entity.components['networked-remote'].data.networkId;
  } else if (components.hasOwnProperty('networked-share')) {
    return entity.components['networked'].data.networkId;
  } else if (components.hasOwnProperty('networked')) {
    return entity.components['networked'].networkId;
  }
  return null;
}

module.exports.getNetworkType = function(entity) {
  var components = entity.components;
  if (components.hasOwnProperty('networked-remote')) {
    return "networked-remote";
  } else if (components.hasOwnProperty('networked-share')) {
    return "networked-share";
  } else if (components.hasOwnProperty('networked')) {
    return "networked";
  }
  return null;
}

module.exports.now = function() {
  return Date.now();
};

module.exports.delimiter = '|||';<commit_msg>use Firebase-friendly delimiter; note that Firebase does not like . or # which are in almost all selectors<commit_after>module.exports.whenEntityLoaded = function(entity, callback) {
  if (entity.hasLoaded) { callback(); }
  entity.addEventListener('loaded', function () {
    callback();
  });
}

module.exports.createHtmlNodeFromString = function(str) {
  var div = document.createElement('div');
  div.innerHTML = str;
  var child = div.firstChild;
  return child;
}

module.exports.getNetworkOwner = function(entity) {
  var components = entity.components;
  if (components.hasOwnProperty('networked-remote')) {
    return entity.components['networked-remote'].data.owner;
  } else if (components.hasOwnProperty('networked')) {
    return entity.components['networked'].owner;
  }
  return null;
}

module.exports.getNetworkId = function(entity) {
  var components = entity.components;
  if (components.hasOwnProperty('networked-remote')) {
    return entity.components['networked-remote'].data.networkId;
  } else if (components.hasOwnProperty('networked-share')) {
    return entity.components['networked'].data.networkId;
  } else if (components.hasOwnProperty('networked')) {
    return entity.components['networked'].networkId;
  }
  return null;
}

module.exports.getNetworkType = function(entity) {
  var components = entity.components;
  if (components.hasOwnProperty('networked-remote')) {
    return "networked-remote";
  } else if (components.hasOwnProperty('networked-share')) {
    return "networked-share";
  } else if (components.hasOwnProperty('networked')) {
    return "networked";
  }
  return null;
}

module.exports.now = function() {
  return Date.now();
};

module.exports.delimiter = '---';
<|endoftext|>


<commit_before>/* global module */

const WebRTC = require('./webrtc')
const WildEmitter = require('wildemitter')
const webrtcSupport = require('webrtcsupport')
const attachMediaStream = require('attachmediastream')
const mockconsole = require('mockconsole')

function SimpleWebRTC(opts) {
	const self = this
	const options = opts || {}
	const config = this.config = {
		socketio: {/* 'force new connection':true */},
		connection: null,
		debug: false,
		localVideoEl: '',
		remoteVideosEl: '',
		enableDataChannels: true,
		autoRequestMedia: false,
		autoRemoveVideos: true,
		adjustPeerVolume: false,
		peerVolumeWhenSpeaking: 0.25,
		media: {
			video: true,
			audio: true,
		},
		receiveMedia: {
			offerToReceiveAudio: 1,
			offerToReceiveVideo: 1,
		},
		localVideo: {
			autoplay: true,
			mirror: true,
			muted: true,
		},
	}
	let item, connection

	// We also allow a 'logger' option. It can be any object that implements
	// log, warn, and error methods.
	// We log nothing by default, following "the rule of silence":
	// http://www.linfo.org/rule_of_silence.html
	this.logger = (function() {
		// we assume that if you're in debug mode and you didn't
		// pass in a logger, you actually want to log as much as
		// possible.
		if (opts.debug) {
			return opts.logger || console
		} else {
		// or we'll use your logger which should have its own logic
		// for output. Or we'll return the no-op.
			return opts.logger || mockconsole
		}
	}())

	// set our config from options
	for (item in options) {
		if (options.hasOwnProperty(item)) {
			this.config[item] = options[item]
		}
	}

	// Override screensharing support detection to fit the custom
	// "getScreenMedia" module.
	// Note that this is a coarse check; calling "getScreenMedia" may fail even
	// if "supportScreenSharing" is true.
	const screenSharingSupported
			= (window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia)
			|| (window.navigator.webkitGetUserMedia)
			|| (window.navigator.userAgent.match('Firefox'))
	webrtcSupport.supportScreenSharing = window.location.protocol === 'https:' && screenSharingSupported

	// attach detected support for convenience
	this.capabilities = webrtcSupport

	// call WildEmitter constructor
	WildEmitter.call(this)

	if (this.config.connection === null) {
		throw new Error('no connection object given in the configuration')
	} else {
		connection = this.connection = this.config.connection
	}

	connection.on('connect', function() {
		self.emit('connectionReady', connection.getSessionId())
		self.sessionReady = true
		self.testReadiness()
	})

	connection.on('message', function(message) {
		const peers = self.webrtc.getPeers(message.from, message.roomType)
		let peer

		if (message.type === 'offer') {
			if (peers.length) {
				peers.forEach(function(p) {
					if (p.sid === message.sid) {
						peer = p
					}
				})
				// if (!peer) peer = peers[0]; // fallback for old protocol versions
			}
			if (!peer) {
				peer = self.webrtc.createPeer({
					id: message.from,
					sid: message.sid,
					type: message.roomType,
					enableDataChannels: self.config.enableDataChannels && message.roomType !== 'screen',
					sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
					broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionId() : null,
					sendVideoIfAvailable: self.connection.getSendVideoIfAvailable(),
				})
				self.emit('createdPeer', peer)
			}
			peer.handleMessage(message)
		} else if (message.type === 'control') {
			if (message.payload.action === 'forceMute') {
				if (message.payload.peerId === self.connection.getSessionId()) {
					self.mute()
					self.emit('forcedMute')
				} else {
					self.emit('mute', { id: message.payload.peerId })
				}
			}
		} else if (peers.length) {
			peers.forEach(function(peer) {
				if (message.sid) {
					if (peer.sid === message.sid) {
						peer.handleMessage(message)
					}
				} else {
					peer.handleMessage(message)
				}
			})
		}
	})

	connection.on('remove', function(room) {
		if (room.id !== self.connection.getSessionId()) {
			self.webrtc.removePeers(room.id, room.type)
		}
	})

	// instantiate our main WebRTC helper
	// using same logger from logic here
	opts.logger = this.logger
	opts.debug = false
	this.webrtc = new WebRTC(opts);

	// attach a few methods from underlying lib to simple.
	['mute', 'unmute', 'pauseVideo', 'resumeVideo', 'pause', 'resume', 'sendToAll', 'sendDirectlyToAll', 'getPeers', 'createPeer', 'removePeers'].forEach(function(method) {
		self[method] = self.webrtc[method].bind(self.webrtc)
	})

	// proxy events from WebRTC
	this.webrtc.on('*', function() {
		self.emit.apply(self, arguments)
	})

	// log all events in debug mode
	if (config.debug) {
		this.on('*', this.logger.log.bind(this.logger, 'SimpleWebRTC event:'))
	}

	// check for readiness
	this.webrtc.on('localStream', function() {
		self.testReadiness()
	})

	this.webrtc.on('message', function(payload) {
		self.connection.emit('message', payload)
	})

	this.webrtc.on('peerStreamAdded', this.handlePeerStreamAdded.bind(this))
	this.webrtc.on('peerStreamRemoved', this.handlePeerStreamRemoved.bind(this))

	// echo cancellation attempts
	if (this.config.adjustPeerVolume) {
		this.webrtc.on('speaking', this.setVolumeForAll.bind(this, this.config.peerVolumeWhenSpeaking))
		this.webrtc.on('stoppedSpeaking', this.setVolumeForAll.bind(this, 1))
	}

	connection.on('stunservers', function(args) {
		// resets/overrides the config
		self.webrtc.config.peerConnectionConfig.iceServers = args
		self.emit('stunservers', args)
	})
	connection.on('turnservers', function(args) {
		// appends to the config
		self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args)
		self.emit('turnservers', args)
	})

	this.webrtc.on('iceFailed', function(/* peer */) {
		// local ice failure
	})
	this.webrtc.on('connectivityError', function(/* peer */) {
		// remote ice failure
	})

	// sending mute/unmute to all peers
	this.webrtc.on('audioOn', function() {
		self.webrtc.sendToAll('unmute', { name: 'audio' })
	})
	this.webrtc.on('audioOff', function() {
		self.webrtc.sendToAll('mute', { name: 'audio' })
	})
	this.webrtc.on('videoOn', function() {
		self.webrtc.sendToAll('unmute', { name: 'video' })
	})
	this.webrtc.on('videoOff', function() {
		self.webrtc.sendToAll('mute', { name: 'video' })
	})

	// screensharing events
	this.webrtc.on('localScreen', function(stream) {
		const el = document.createElement('video')
		const container = self.getRemoteVideoContainer()

		el.oncontextmenu = function() { return false }
		el.id = 'localScreen'
		attachMediaStream(stream, el)
		if (container) {
			container.appendChild(el)
		}

		self.emit('localScreenAdded', el)
		self.connection.emit('shareScreen')

		// NOTE: we don't create screen peers for existing video peers here,
		// this is done by the application code in "webrtc.js".
	})
	this.webrtc.on('localScreenStopped', function(/* stream */) {
		self.stopScreenShare()
		/*
		self.connection.emit('unshareScreen');
		self.webrtc.peers.forEach(function (peer) {
			if (peer.sharemyscreen) {
				peer.end();
			}
		});
		*/
	})
}

SimpleWebRTC.prototype = Object.create(WildEmitter.prototype, {
	constructor: {
		value: SimpleWebRTC,
	},
})

SimpleWebRTC.prototype.leaveCall = function() {
	if (this.roomName) {
		while (this.webrtc.peers.length) {
			this.webrtc.peers[0].end()
		}
		if (this.getLocalScreen()) {
			this.stopScreenShare()
		}
		this.emit('leftRoom', this.roomName)
		this.stopLocalVideo()
		this.roomName = undefined
	}
}

SimpleWebRTC.prototype.disconnect = function() {
	this.emit('disconnected')
}

SimpleWebRTC.prototype.handlePeerStreamAdded = function(peer) {
	const self = this
	const container = this.getRemoteVideoContainer()
	if (container) {
		// If there is a video track Chromium does not play audio in a video element
		// until the video track starts to play; an audio element is thus needed to
		// play audio when the remote peer starts with the camera available but
		// disabled.
		const audio = attachMediaStream(peer.stream, null, { audio: true })
		const video = attachMediaStream(peer.stream)

		video.muted = true

		// At least Firefox, Opera and Edge move the video to a wrong position
		// instead of keeping it unchanged when "transform: scaleX(1)" is used
		// ("transform: scaleX(-1)" is fine); as it should have no effect the
		// transform is removed.
		if (video.style.transform === 'scaleX(1)') {
			video.style.transform = ''
		}

		// store video element as part of peer for easy removal
		peer.audioEl = audio
		peer.videoEl = video
		audio.id = this.getDomId(peer) + '-audio'
		video.id = this.getDomId(peer)

		container.appendChild(audio)
		container.appendChild(video)

		this.emit('videoAdded', video, audio, peer)
	}

	// send our mute status to new peer if we're muted
	// currently called with a small delay because it arrives before
	// the video element is created otherwise (which happens after
	// the async setRemoteDescription-createAnswer)
	window.setTimeout(function() {
		if (!self.webrtc.isAudioEnabled()) {
			peer.send('mute', { name: 'audio' })
		}
		if (!self.webrtc.isVideoEnabled()) {
			peer.send('mute', { name: 'video' })
		}
	}, 250)
}

SimpleWebRTC.prototype.handlePeerStreamRemoved = function(peer) {
	const container = this.getRemoteVideoContainer()
	const audioEl = peer.audioEl
	const videoEl = peer.videoEl
	if (this.config.autoRemoveVideos && container && audioEl) {
		container.removeChild(audioEl)
	}
	if (this.config.autoRemoveVideos && container && videoEl) {
		container.removeChild(videoEl)
	}
	if (videoEl) {
		this.emit('videoRemoved', videoEl, peer)
	}
}

SimpleWebRTC.prototype.getDomId = function(peer) {
	return [peer.id, peer.type, peer.broadcaster ? 'broadcasting' : 'incoming'].join('_')
}

// set volume on video tag for all peers takse a value between 0 and 1
SimpleWebRTC.prototype.setVolumeForAll = function(volume) {
	this.webrtc.peers.forEach(function(peer) {
		if (peer.audioEl) {
			peer.audioEl.volume = volume
		}
	})
}

SimpleWebRTC.prototype.joinCall = function(name) {
	if (this.config.autoRequestMedia) {
		this.startLocalVideo()
	}
	this.roomName = name
	this.emit('joinedRoom', name)
}

SimpleWebRTC.prototype.getEl = function(idOrEl) {
	if (typeof idOrEl === 'string') {
		return document.getElementById(idOrEl)
	} else {
		return idOrEl
	}
}

SimpleWebRTC.prototype.startLocalVideo = function() {
	const self = this
	this.webrtc.start(this.config.media, function(err, stream) {
		if (err) {
			self.emit('localMediaError', err)
		} else {
			self.emit('localMediaStarted', self.config.media)

			const localVideoContainer = self.getLocalVideoContainer()
			if (localVideoContainer) {
				attachMediaStream(stream, localVideoContainer, self.config.localVideo)
			}
		}
	})
}

SimpleWebRTC.prototype.stopLocalVideo = function() {
	this.webrtc.stop()
}

// this accepts either element ID or element
// and either the video tag itself or a container
// that will be used to put the video tag into.
SimpleWebRTC.prototype.getLocalVideoContainer = function() {
	const el = this.getEl(this.config.localVideoEl)
	if (el && el.tagName === 'VIDEO') {
		el.oncontextmenu = function() { return false }
		return el
	} else if (el) {
		const video = document.createElement('video')
		video.oncontextmenu = function() { return false }
		el.appendChild(video)
		return video
	}
}

SimpleWebRTC.prototype.getRemoteVideoContainer = function() {
	return this.getEl(this.config.remoteVideosEl)
}

SimpleWebRTC.prototype.shareScreen = function(mode, cb) {
	this.webrtc.startScreenShare(mode, cb)
}

SimpleWebRTC.prototype.getLocalScreen = function() {
	return this.webrtc.localScreen
}

SimpleWebRTC.prototype.stopScreenShare = function() {
	this.connection.emit('unshareScreen')
	const videoEl = document.getElementById('localScreen')
	const container = this.getRemoteVideoContainer()

	if (this.config.autoRemoveVideos && container && videoEl) {
		container.removeChild(videoEl)
	}

	// a hack to emit the event the removes the video
	// element that we want
	if (videoEl) {
		this.emit('videoRemoved', videoEl)
	}
	if (this.getLocalScreen()) {
		this.webrtc.stopScreenShare()
	}
	// Notify peers were sending to.
	this.webrtc.peers.forEach(function(peer) {
		if (peer.type === 'screen' && peer.sharemyscreen) {
			peer.send('unshareScreen')
		}
		if (peer.broadcaster) {
			peer.end()
		}
	})
}

SimpleWebRTC.prototype.testReadiness = function() {
	const self = this
	if (this.sessionReady) {
		if (!this.config.media.video && !this.config.media.audio) {
			self.emit('readyToCall', self.connection.getSessionId())
		} else if (this.webrtc.localStreams.length > 0) {
			self.emit('readyToCall', self.connection.getSessionId())
		}
	}
}

SimpleWebRTC.prototype.createRoom = function(name, cb) {
	this.roomName = name
	if (arguments.length === 2) {
		this.connection.emit('create', name, cb)
	} else {
		this.connection.emit('create', name)
	}
}

module.exports = SimpleWebRTC
<commit_msg>Remove unused code<commit_after>/* global module */

const WebRTC = require('./webrtc')
const WildEmitter = require('wildemitter')
const webrtcSupport = require('webrtcsupport')
const attachMediaStream = require('attachmediastream')
const mockconsole = require('mockconsole')

function SimpleWebRTC(opts) {
	const self = this
	const options = opts || {}
	const config = this.config = {
		socketio: {/* 'force new connection':true */},
		connection: null,
		debug: false,
		localVideoEl: '',
		remoteVideosEl: '',
		enableDataChannels: true,
		autoRequestMedia: false,
		autoRemoveVideos: true,
		adjustPeerVolume: false,
		peerVolumeWhenSpeaking: 0.25,
		media: {
			video: true,
			audio: true,
		},
		receiveMedia: {
			offerToReceiveAudio: 1,
			offerToReceiveVideo: 1,
		},
		localVideo: {
			autoplay: true,
			mirror: true,
			muted: true,
		},
	}
	let item, connection

	// We also allow a 'logger' option. It can be any object that implements
	// log, warn, and error methods.
	// We log nothing by default, following "the rule of silence":
	// http://www.linfo.org/rule_of_silence.html
	this.logger = (function() {
		// we assume that if you're in debug mode and you didn't
		// pass in a logger, you actually want to log as much as
		// possible.
		if (opts.debug) {
			return opts.logger || console
		} else {
		// or we'll use your logger which should have its own logic
		// for output. Or we'll return the no-op.
			return opts.logger || mockconsole
		}
	}())

	// set our config from options
	for (item in options) {
		if (options.hasOwnProperty(item)) {
			this.config[item] = options[item]
		}
	}

	// Override screensharing support detection to fit the custom
	// "getScreenMedia" module.
	// Note that this is a coarse check; calling "getScreenMedia" may fail even
	// if "supportScreenSharing" is true.
	const screenSharingSupported
			= (window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia)
			|| (window.navigator.webkitGetUserMedia)
			|| (window.navigator.userAgent.match('Firefox'))
	webrtcSupport.supportScreenSharing = window.location.protocol === 'https:' && screenSharingSupported

	// attach detected support for convenience
	this.capabilities = webrtcSupport

	// call WildEmitter constructor
	WildEmitter.call(this)

	if (this.config.connection === null) {
		throw new Error('no connection object given in the configuration')
	} else {
		connection = this.connection = this.config.connection
	}

	connection.on('message', function(message) {
		const peers = self.webrtc.getPeers(message.from, message.roomType)
		let peer

		if (message.type === 'offer') {
			if (peers.length) {
				peers.forEach(function(p) {
					if (p.sid === message.sid) {
						peer = p
					}
				})
				// if (!peer) peer = peers[0]; // fallback for old protocol versions
			}
			if (!peer) {
				peer = self.webrtc.createPeer({
					id: message.from,
					sid: message.sid,
					type: message.roomType,
					enableDataChannels: self.config.enableDataChannels && message.roomType !== 'screen',
					sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
					broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionId() : null,
					sendVideoIfAvailable: self.connection.getSendVideoIfAvailable(),
				})
				self.emit('createdPeer', peer)
			}
			peer.handleMessage(message)
		} else if (message.type === 'control') {
			if (message.payload.action === 'forceMute') {
				if (message.payload.peerId === self.connection.getSessionId()) {
					self.mute()
					self.emit('forcedMute')
				} else {
					self.emit('mute', { id: message.payload.peerId })
				}
			}
		} else if (peers.length) {
			peers.forEach(function(peer) {
				if (message.sid) {
					if (peer.sid === message.sid) {
						peer.handleMessage(message)
					}
				} else {
					peer.handleMessage(message)
				}
			})
		}
	})

	connection.on('remove', function(room) {
		if (room.id !== self.connection.getSessionId()) {
			self.webrtc.removePeers(room.id, room.type)
		}
	})

	// instantiate our main WebRTC helper
	// using same logger from logic here
	opts.logger = this.logger
	opts.debug = false
	this.webrtc = new WebRTC(opts);

	// attach a few methods from underlying lib to simple.
	['mute', 'unmute', 'pauseVideo', 'resumeVideo', 'pause', 'resume', 'sendToAll', 'sendDirectlyToAll', 'getPeers', 'createPeer', 'removePeers'].forEach(function(method) {
		self[method] = self.webrtc[method].bind(self.webrtc)
	})

	// proxy events from WebRTC
	this.webrtc.on('*', function() {
		self.emit.apply(self, arguments)
	})

	// log all events in debug mode
	if (config.debug) {
		this.on('*', this.logger.log.bind(this.logger, 'SimpleWebRTC event:'))
	}

	this.webrtc.on('message', function(payload) {
		self.connection.emit('message', payload)
	})

	this.webrtc.on('peerStreamAdded', this.handlePeerStreamAdded.bind(this))
	this.webrtc.on('peerStreamRemoved', this.handlePeerStreamRemoved.bind(this))

	// echo cancellation attempts
	if (this.config.adjustPeerVolume) {
		this.webrtc.on('speaking', this.setVolumeForAll.bind(this, this.config.peerVolumeWhenSpeaking))
		this.webrtc.on('stoppedSpeaking', this.setVolumeForAll.bind(this, 1))
	}

	connection.on('stunservers', function(args) {
		// resets/overrides the config
		self.webrtc.config.peerConnectionConfig.iceServers = args
		self.emit('stunservers', args)
	})
	connection.on('turnservers', function(args) {
		// appends to the config
		self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args)
		self.emit('turnservers', args)
	})

	this.webrtc.on('iceFailed', function(/* peer */) {
		// local ice failure
	})
	this.webrtc.on('connectivityError', function(/* peer */) {
		// remote ice failure
	})

	// sending mute/unmute to all peers
	this.webrtc.on('audioOn', function() {
		self.webrtc.sendToAll('unmute', { name: 'audio' })
	})
	this.webrtc.on('audioOff', function() {
		self.webrtc.sendToAll('mute', { name: 'audio' })
	})
	this.webrtc.on('videoOn', function() {
		self.webrtc.sendToAll('unmute', { name: 'video' })
	})
	this.webrtc.on('videoOff', function() {
		self.webrtc.sendToAll('mute', { name: 'video' })
	})

	// screensharing events
	this.webrtc.on('localScreen', function(stream) {
		const el = document.createElement('video')
		const container = self.getRemoteVideoContainer()

		el.oncontextmenu = function() { return false }
		el.id = 'localScreen'
		attachMediaStream(stream, el)
		if (container) {
			container.appendChild(el)
		}

		self.emit('localScreenAdded', el)
		self.connection.emit('shareScreen')

		// NOTE: we don't create screen peers for existing video peers here,
		// this is done by the application code in "webrtc.js".
	})
	this.webrtc.on('localScreenStopped', function(/* stream */) {
		self.stopScreenShare()
		/*
		self.connection.emit('unshareScreen');
		self.webrtc.peers.forEach(function (peer) {
			if (peer.sharemyscreen) {
				peer.end();
			}
		});
		*/
	})
}

SimpleWebRTC.prototype = Object.create(WildEmitter.prototype, {
	constructor: {
		value: SimpleWebRTC,
	},
})

SimpleWebRTC.prototype.leaveCall = function() {
	if (this.roomName) {
		while (this.webrtc.peers.length) {
			this.webrtc.peers[0].end()
		}
		if (this.getLocalScreen()) {
			this.stopScreenShare()
		}
		this.emit('leftRoom', this.roomName)
		this.stopLocalVideo()
		this.roomName = undefined
	}
}

SimpleWebRTC.prototype.disconnect = function() {
	this.emit('disconnected')
}

SimpleWebRTC.prototype.handlePeerStreamAdded = function(peer) {
	const self = this
	const container = this.getRemoteVideoContainer()
	if (container) {
		// If there is a video track Chromium does not play audio in a video element
		// until the video track starts to play; an audio element is thus needed to
		// play audio when the remote peer starts with the camera available but
		// disabled.
		const audio = attachMediaStream(peer.stream, null, { audio: true })
		const video = attachMediaStream(peer.stream)

		video.muted = true

		// At least Firefox, Opera and Edge move the video to a wrong position
		// instead of keeping it unchanged when "transform: scaleX(1)" is used
		// ("transform: scaleX(-1)" is fine); as it should have no effect the
		// transform is removed.
		if (video.style.transform === 'scaleX(1)') {
			video.style.transform = ''
		}

		// store video element as part of peer for easy removal
		peer.audioEl = audio
		peer.videoEl = video
		audio.id = this.getDomId(peer) + '-audio'
		video.id = this.getDomId(peer)

		container.appendChild(audio)
		container.appendChild(video)

		this.emit('videoAdded', video, audio, peer)
	}

	// send our mute status to new peer if we're muted
	// currently called with a small delay because it arrives before
	// the video element is created otherwise (which happens after
	// the async setRemoteDescription-createAnswer)
	window.setTimeout(function() {
		if (!self.webrtc.isAudioEnabled()) {
			peer.send('mute', { name: 'audio' })
		}
		if (!self.webrtc.isVideoEnabled()) {
			peer.send('mute', { name: 'video' })
		}
	}, 250)
}

SimpleWebRTC.prototype.handlePeerStreamRemoved = function(peer) {
	const container = this.getRemoteVideoContainer()
	const audioEl = peer.audioEl
	const videoEl = peer.videoEl
	if (this.config.autoRemoveVideos && container && audioEl) {
		container.removeChild(audioEl)
	}
	if (this.config.autoRemoveVideos && container && videoEl) {
		container.removeChild(videoEl)
	}
	if (videoEl) {
		this.emit('videoRemoved', videoEl, peer)
	}
}

SimpleWebRTC.prototype.getDomId = function(peer) {
	return [peer.id, peer.type, peer.broadcaster ? 'broadcasting' : 'incoming'].join('_')
}

// set volume on video tag for all peers takse a value between 0 and 1
SimpleWebRTC.prototype.setVolumeForAll = function(volume) {
	this.webrtc.peers.forEach(function(peer) {
		if (peer.audioEl) {
			peer.audioEl.volume = volume
		}
	})
}

SimpleWebRTC.prototype.joinCall = function(name) {
	if (this.config.autoRequestMedia) {
		this.startLocalVideo()
	}
	this.roomName = name
	this.emit('joinedRoom', name)
}

SimpleWebRTC.prototype.getEl = function(idOrEl) {
	if (typeof idOrEl === 'string') {
		return document.getElementById(idOrEl)
	} else {
		return idOrEl
	}
}

SimpleWebRTC.prototype.startLocalVideo = function() {
	const self = this
	this.webrtc.start(this.config.media, function(err, stream) {
		if (err) {
			self.emit('localMediaError', err)
		} else {
			self.emit('localMediaStarted', self.config.media)

			const localVideoContainer = self.getLocalVideoContainer()
			if (localVideoContainer) {
				attachMediaStream(stream, localVideoContainer, self.config.localVideo)
			}
		}
	})
}

SimpleWebRTC.prototype.stopLocalVideo = function() {
	this.webrtc.stop()
}

// this accepts either element ID or element
// and either the video tag itself or a container
// that will be used to put the video tag into.
SimpleWebRTC.prototype.getLocalVideoContainer = function() {
	const el = this.getEl(this.config.localVideoEl)
	if (el && el.tagName === 'VIDEO') {
		el.oncontextmenu = function() { return false }
		return el
	} else if (el) {
		const video = document.createElement('video')
		video.oncontextmenu = function() { return false }
		el.appendChild(video)
		return video
	}
}

SimpleWebRTC.prototype.getRemoteVideoContainer = function() {
	return this.getEl(this.config.remoteVideosEl)
}

SimpleWebRTC.prototype.shareScreen = function(mode, cb) {
	this.webrtc.startScreenShare(mode, cb)
}

SimpleWebRTC.prototype.getLocalScreen = function() {
	return this.webrtc.localScreen
}

SimpleWebRTC.prototype.stopScreenShare = function() {
	this.connection.emit('unshareScreen')
	const videoEl = document.getElementById('localScreen')
	const container = this.getRemoteVideoContainer()

	if (this.config.autoRemoveVideos && container && videoEl) {
		container.removeChild(videoEl)
	}

	// a hack to emit the event the removes the video
	// element that we want
	if (videoEl) {
		this.emit('videoRemoved', videoEl)
	}
	if (this.getLocalScreen()) {
		this.webrtc.stopScreenShare()
	}
	// Notify peers were sending to.
	this.webrtc.peers.forEach(function(peer) {
		if (peer.type === 'screen' && peer.sharemyscreen) {
			peer.send('unshareScreen')
		}
		if (peer.broadcaster) {
			peer.end()
		}
	})
}

SimpleWebRTC.prototype.createRoom = function(name, cb) {
	this.roomName = name
	if (arguments.length === 2) {
		this.connection.emit('create', name, cb)
	} else {
		this.connection.emit('create', name)
	}
}

module.exports = SimpleWebRTC
<|endoftext|>