<commit_before>Strophe.addConnectionPlugin('jingle', {
    connection: null,
    sessions: {},
    jid2session: {},
    ice_config: {iceServers: []},
    pc_constraints: {},
    media_constraints: {'mandatory': {
        'OfferToReceiveAudio': true,
        'OfferToReceiveVideo': true }
        // MozDontOfferDataChannel: true when this is firefox
    },
    localStream: null,
    MULTIPARTY: false,
    AUTOACCEPT: true,
    PRANSWER: false,

    init: function(conn) {
        this.connection = conn;
        if (this.connection.disco) {
            // http://xmpp.org/extensions/xep-0167.html#support
            // http://xmpp.org/extensions/xep-0176.html#support
            this.connection.disco.addFeature('urn:xmpp:jingle:1');
            this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:1');
            this.connection.disco.addFeature('urn:xmpp:jingle:transports:ice-udp:1');
            this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:audio');
            this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:video');

            this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux

            // well, this is canary only yet -- and dealt with by SDP O/A so it is not
            // necessary to add this
            //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
            //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
        }
        this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
    },
    onJingle: function(iq) {
        var sid = $(iq).find('jingle').attr('sid');
        var action = $(iq).find('jingle').attr('action');
        // send ack first
        var ack = $iq({type: 'result',
              to: iq.getAttribute('from'),
              id: iq.getAttribute('id')
        });
        console.log('on jingle ' + action);
        if ('session-initiate' != action && !(sid in this.sessions)) {
            ack.type = 'error';
            ack.c('error', {type: 'cancel'})
               .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
               .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
            this.connection.send(ack);
            return true;
        }
        this.connection.send(ack);
        var sess = this.sessions[sid];
        // see http://xmpp.org/extensions/xep-0166.html#concepts-session
        switch (action) {
        case 'session-initiate':
            if (this.MULTIPARTY || Object.keys(this.sessions).length == 0) {
                sess = new JingleSession($(iq).attr('to'), $(iq).find('jingle').attr('sid'), this.connection);
                // configure session
                sess.localStream = this.localStream;
                sess.media_constraints = this.media_constraints;
                sess.pc_constraints = this.pc_constraints;
                sess.ice_config = this.ice_config;

                sess.initiate($(iq).attr('from'), false);
                // FIXME: setRemoteDescription should only be done when this call is to be accepted
                sess.setRemoteDescription($(iq).find('>jingle>content'), 'offer');

                this.sessions[sess.sid] = sess;
                this.jid2session[sess.peerjid] = sess;

                $(document).trigger('callincoming.jingle', [sess.sid]);

                // FIXME: this should be a callback based on the jid
                if (this.AUTOACCEPT) {
                    sess.sendAnswer();
                    sess.accept();
                    // FIXME: watch for unavailable from this specific jid to terminate properly and remove handler later
                    //  currently done by app + terminateByJid
                    // hand = this.connection.addHandler(onPresenceUnavailable, null, 'presence', 'unavailable', null, roomjid, {matchBare: true});
                } else if (this.PRANSWER) {
                    sess.sendAnswer(true);
                }
            } else {
                sess = new JingleSession($(iq).attr('to'), $(iq).find('jingle').attr('sid'), this.connection);
                sess.peerjid = $(iq).attr('from');
                sess.sendTerminate('busy');
                sess.terminate();
                delete sess;
            }
            break;
        case 'session-accept':
            sess.setRemoteDescription($(iq).find('>jingle>content'), 'answer');
            sess.accept();
            break;
        case 'session-terminate':
            console.log('terminating...');
            sess.terminate();
            this.terminate(sess.sid);
            if ($(iq).find('>jingle>reason>').length) {
                $(document).trigger('callterminated.jingle', [
                    sess.sid,
                    $(iq).find('>jingle>reason>')[0].tagName,
                    $(iq).find('>jingle>reason>text').text()
                ]);
            } else {
                $(document).trigger('callterminated.jingle', [sess.sid]);
            }
            break;
        case 'transport-info':
            sess.addIceCandidate($(iq).find('>jingle>content'));
            break;
        case 'session-info':
            if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]')) {
                $(document).trigger('callringing.jingle', [sess.sid]);
            }
            break;
        default:
            console.warn('jingle action not implemented', action);
            break;
        }
        return true;
    },
    initiate: function(peerjid, myjid, callback) { // initiate a new jinglesession to peerjid
        var sess = new JingleSession(myjid,
                                     Math.random().toString(36).substr(2, 12), // random string
                                     this.connection);
        // configure session
        sess.localStream = this.localStream;
        sess.media_constraints = this.media_constraints;
        sess.pc_constraints = this.pc_constraints;
        sess.ice_config = this.ice_config;

        sess.initiate(peerjid, true);
        this.sessions[sess.sid] = sess;
        this.jid2session[sess.peerjid] = sess;
        sess.sendOffer(callback);
        return sess;
    },
    terminate: function(sid, reason, text) { // terminate by sessionid (or all sessions)
        if (sid == null) {
            for (sid in this.sessions) {
                if(this.sessions[sid].state != 'ended'){
                    this.sessions[sid].sendTerminate(reason||(!this.sessions[sid].active())?'cancel':null, text);
                    this.sessions[sid].terminate();
                }
                delete this.jid2session[this.sessions[sid].peerjid];
                delete this.sessions[sid];
            }
        } else if (this.sessions.hasOwnProperty(sid)) {
            if(this.sessions[sid].state != 'ended'){
                this.sessions[sid].sendTerminate(reason||(!this.sessions[sid].active())?'cancel':null, text);
                this.sessions[sid].terminate();
            }
            delete this.jid2session[this.sessions[sid].peerjid];
            delete this.sessions[sid];
        }
    },
    terminateByJid: function(jid) {
        if (this.jid2session.hasOwnProperty(jid)) {
            var sess = this.jid2session[jid];
            if (sess) {
                sess.terminate();
                console.log('peer went away silently', jid);
                delete this.sessions[sess.sid];
                delete this.jid2session[jid];
                $(document).trigger('callterminated.jingle', [sess.sid, 'gone']);
            }
        }
    },
    getStunAndTurnCredentials: function() {
        // get stun and turn configuration from server via xep-0215
        // uses time-limited credentials as described in
        // https://docs.google.com/document/d/1mG7eXFQ5o-ypMWQ1IzdkBQL0UBkLN1xXUJhJcIF5ujQ/edit
        //
        // see https://code.google.com/p/prosody-modules/source/browse/mod_turncredentials/mod_turncredentials.lua
        // for a prosody module which implements this
        //
        // currently, this doesn't work with updateIce and therefore credentials with a long
        // validity have to be fetched before creating the peerconnection
        // TODO: implement refresh via updateIce as described in
        //      https://code.google.com/p/webrtc/issues/detail?id=1650
        this.connection.sendIQ(
            $iq({type: 'get', to: this.connection.domain})
                .c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + this.connection.domain}),
            function(res) {
                var iceservers = [];
                $(res).find('>services>service').each(function(idx, el) {
                    el = $(el);
                    var dict = {};
                    switch (el.attr('type')) {
                    case 'stun':
                        dict.url = 'stun:' + el.attr('host');
                        if (el.attr('port')) {
                            dict.url += ':' + el.attr('port');
                        }
                        iceservers.push(dict);
                        break;
                    case 'turn':
                        dict.url = 'turn:';
                        if (el.attr('username')) { // https://code.google.com/p/webrtc/issues/detail?id=1508
                            // dict.username = el.attr('username'); // only works in M28
                            dict.url += el.attr('username') + '@';
                        }
                        dict.url += el.attr('host');
                        if (el.attr('password')) {
                            dict.credential = el.attr('password');
                        }
                        iceservers.push(dict);
                        break;
                    }
                });
                this.ice_config.iceServers = iceservers;
            },
            function(err) {
                console.warn('getting turn credentials failed', err);
                console.warn('is mod_turncredentials or similar installed?');
            }
        );
        // implement push?
    }
});
<commit_msg>fetch from upstream<commit_after>Strophe.addConnectionPlugin('jingle', {
    connection: null,
    sessions: {},
    jid2session: {},
    ice_config: {iceServers: []},
    pc_constraints: {},
    media_constraints: {'mandatory': {
        'OfferToReceiveAudio': true,
        'OfferToReceiveVideo': true }
        // MozDontOfferDataChannel: true when this is firefox
    },
    localStream: null,
    MULTIPARTY: false,
    AUTOACCEPT: true,
    PRANSWER: false,

    init: function(conn) {
        this.connection = conn;
        if (this.connection.disco) {
            // http://xmpp.org/extensions/xep-0167.html#support
            // http://xmpp.org/extensions/xep-0176.html#support
            this.connection.disco.addFeature('urn:xmpp:jingle:1');
            this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:1');
            this.connection.disco.addFeature('urn:xmpp:jingle:transports:ice-udp:1');
            this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:audio');
            this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:video');

            this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux

            // well, this is canary only yet -- and dealt with by SDP O/A so it is not
            // necessary to add this
            //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
            //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
        }
        this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
    },
    onJingle: function(iq) {
        var sid = $(iq).find('jingle').attr('sid');
        var action = $(iq).find('jingle').attr('action');
        // send ack first
        var ack = $iq({type: 'result',
              to: iq.getAttribute('from'),
              id: iq.getAttribute('id')
        });
        console.log('on jingle ' + action);
        if ('session-initiate' != action && !(sid in this.sessions)) {
            ack.type = 'error';
            ack.c('error', {type: 'cancel'})
               .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
               .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
            this.connection.send(ack);
            return true;
        }
        this.connection.send(ack);
        var sess = this.sessions[sid];
        // see http://xmpp.org/extensions/xep-0166.html#concepts-session
        switch (action) {
        case 'session-initiate':
            if (this.MULTIPARTY || Object.keys(this.sessions).length == 0) {
                sess = new JingleSession($(iq).attr('to'), $(iq).find('jingle').attr('sid'), this.connection);
                // configure session
                sess.localStream = this.localStream;
                sess.media_constraints = this.media_constraints;
                sess.pc_constraints = this.pc_constraints;
                sess.ice_config = this.ice_config;

                sess.initiate($(iq).attr('from'), false);
                // FIXME: setRemoteDescription should only be done when this call is to be accepted
                sess.setRemoteDescription($(iq).find('>jingle>content'), 'offer');

                this.sessions[sess.sid] = sess;
                this.jid2session[sess.peerjid] = sess;

                $(document).trigger('callincoming.jingle', [sess.sid]);

                // FIXME: this should be a callback based on the jid
                if (this.AUTOACCEPT) {
                    sess.sendAnswer();
                    sess.accept();
                    // FIXME: watch for unavailable from this specific jid to terminate properly and remove handler later
                    //  currently done by app + terminateByJid
                    // hand = this.connection.addHandler(onPresenceUnavailable, null, 'presence', 'unavailable', null, roomjid, {matchBare: true});
                } else if (this.PRANSWER) {
                    sess.sendAnswer(true);
                }
            } else {
                sess = new JingleSession($(iq).attr('to'), $(iq).find('jingle').attr('sid'), this.connection);
                sess.peerjid = $(iq).attr('from');
                sess.sendTerminate('busy');
                sess.terminate();
                delete sess;
            }
            break;
        case 'session-accept':
            sess.setRemoteDescription($(iq).find('>jingle>content'), 'answer');
            sess.accept();
            break;
        case 'session-terminate':
            console.log('terminating...');
            sess.terminate();
            this.terminate(sess.sid);
            if ($(iq).find('>jingle>reason>').length) {
                $(document).trigger('callterminated.jingle', [
                    sess.sid,
                    $(iq).find('>jingle>reason>')[0].tagName,
                    $(iq).find('>jingle>reason>text').text()
                ]);
            } else {
                $(document).trigger('callterminated.jingle', [sess.sid]);
            }
            break;
        case 'transport-info':
            sess.addIceCandidate($(iq).find('>jingle>content'));
            break;
        case 'session-info':
            if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
                $(document).trigger('ringing.jingle', [sess.sid]);
            } else if ($(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
                var affected = $(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
                $(document).trigger('mute.jingle', [sess.sid, affected]);
            } else if ($(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
                var affected = $(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
                $(document).trigger('unmute.jingle', [sess.sid, affected]);
            }
            break;
        default:
            console.warn('jingle action not implemented', action);
            break;
        }
        return true;
    },
    initiate: function(peerjid, myjid, callback) { // initiate a new jinglesession to peerjid
        var sess = new JingleSession(myjid,
                                     Math.random().toString(36).substr(2, 12), // random string
                                     this.connection);
        // configure session
        sess.localStream = this.localStream;
        sess.media_constraints = this.media_constraints;
        sess.pc_constraints = this.pc_constraints;
        sess.ice_config = this.ice_config;

        sess.initiate(peerjid, true);
        this.sessions[sess.sid] = sess;
        this.jid2session[sess.peerjid] = sess;
        sess.sendOffer(callback);
        return sess;
    },
    terminate: function(sid, reason, text) { // terminate by sessionid (or all sessions)
        if (sid == null) {
            for (sid in this.sessions) {
                if(this.sessions[sid].state != 'ended'){
                    this.sessions[sid].sendTerminate(reason||(!this.sessions[sid].active())?'cancel':null, text);
                    this.sessions[sid].terminate();
                }
                delete this.jid2session[this.sessions[sid].peerjid];
                delete this.sessions[sid];
            }
        } else if (this.sessions.hasOwnProperty(sid)) {
            if(this.sessions[sid].state != 'ended'){
                this.sessions[sid].sendTerminate(reason||(!this.sessions[sid].active())?'cancel':null, text);
                this.sessions[sid].terminate();
            }
            delete this.jid2session[this.sessions[sid].peerjid];
            delete this.sessions[sid];
        }
    },
    terminateByJid: function(jid) {
        if (this.jid2session.hasOwnProperty(jid)) {
            var sess = this.jid2session[jid];
            if (sess) {
                sess.terminate();
                console.log('peer went away silently', jid);
                delete this.sessions[sess.sid];
                delete this.jid2session[jid];
                $(document).trigger('callterminated.jingle', [sess.sid, 'gone']);
            }
        }
    },
    getStunAndTurnCredentials: function() {
        // get stun and turn configuration from server via xep-0215
        // uses time-limited credentials as described in
        // https://docs.google.com/document/d/1mG7eXFQ5o-ypMWQ1IzdkBQL0UBkLN1xXUJhJcIF5ujQ/edit
        //
        // see https://code.google.com/p/prosody-modules/source/browse/mod_turncredentials/mod_turncredentials.lua
        // for a prosody module which implements this
        //
        // currently, this doesn't work with updateIce and therefore credentials with a long
        // validity have to be fetched before creating the peerconnection
        // TODO: implement refresh via updateIce as described in
        //      https://code.google.com/p/webrtc/issues/detail?id=1650
        this.connection.sendIQ(
            $iq({type: 'get', to: this.connection.domain})
                .c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + this.connection.domain}),
            function(res) {
                var iceservers = [];
                $(res).find('>services>service').each(function(idx, el) {
                    el = $(el);
                    var dict = {};
                    switch (el.attr('type')) {
                    case 'stun':
                        dict.url = 'stun:' + el.attr('host');
                        if (el.attr('port')) {
                            dict.url += ':' + el.attr('port');
                        }
                        iceservers.push(dict);
                        break;
                    case 'turn':
                        dict.url = 'turn:';
                        if (el.attr('username')) { // https://code.google.com/p/webrtc/issues/detail?id=1508
                            // dict.username = el.attr('username'); // only works in M28
                            dict.url += el.attr('username') + '@';
                        }
                        dict.url += el.attr('host');
                        if (el.attr('password')) {
                            dict.credential = el.attr('password');
                        }
                        iceservers.push(dict);
                        break;
                    }
                });
                this.ice_config.iceServers = iceservers;
            },
            function(err) {
                console.warn('getting turn credentials failed', err);
                console.warn('is mod_turncredentials or similar installed?');
            }
        );
        // implement push?
    }
});
<|endoftext|>


<commit_before>// Version: 2.1
// Latest Source: https://github.com/Czarto/Adwords-Scripts/blob/master/device-bid-adjustments.js
//
// This Google Ads Script will incrementally change device bid adjustments
// based on conversion rates using the Campaign's average conversion rate
// as a baseline.
//

/***********

MIT License

Copyright (c) 2016-2021 Alex Czartoryski

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

**********/

var LABEL_PROCESSING = "_processing_desktop";
var LABEL_PROCESSING_RESOURCE = "";

var BID_INCREMENT = 0.05;       // Value by which to adjust bids
var MIN_CONVERSIONS = 10;       // Minimum conversions needed to adjust bids.
var MAX_BID_ADJUSTMENT = 1.90;  // Do not increase adjustments above this value

function main() {
    initLabels(); // Create Labels

    /*****
      Device performance *should* theoretically not vary over time
      (unless a site redesign has been performed) and so it makes
      most sense to use a relatively long time period (1 year)
      on which to base adjustments.

      Shorter time periods included for reference, but commented out
    *****/

    //setDeviceBidModifier("LAST_7_DAYS");
    //setDeviceBidModifier("LAST_14_DAYS");
    //setDeviceBidModifier("LAST_30_DAYS");
    //setDeviceBidModifier(LAST_90_DAYS(), TODAY());
    setDeviceBidModifier(LAST_YEAR(), TODAY());
    setDeviceBidModifier("ALL_TIME");

    cleanup(); // Remove Labels

    // TODO: Only process enabled campaigns/ad groups

}

//
// Set the Processing label
// This keeps track of which bid adjustments have already been processed
// in the case where multiple time-lookback windows are being used
//
function initLabels() {
    checkLabelExists();
    cleanup();

    var itemsToLabel = [AdsApp.campaigns(), AdsApp.shoppingCampaigns()];

    for (i = 0; i < itemsToLabel.length; i++) {
        var iterator = itemsToLabel[i].withCondition("Status = ENABLED").get();

        while (iterator.hasNext()) {
            iterator.next().applyLabel(LABEL_PROCESSING);
          }
    }
}

//
// Create the processing label if it does not exist
//
function checkLabelExists() {
    // Logger.log('CheckLabelExists()');

    var labelIterator = AdsApp.labels().withCondition("Name = '" + LABEL_PROCESSING + "'" ).get();

    if( !labelIterator.hasNext()) {
        AdsApp.createLabel(LABEL_PROCESSING, "AdWords Scripts label used to process bids")
    }

    // Get label resource Ids (for beta experience script)
    /* Beta
    var labelIterator = AdsApp.labels().withCondition("Name = '" + LABEL_PROCESSING "'").get();
    if (labelIterator.hasNext()) {
        LABEL_PROCESSING_RESOURCE = labelIterator.next().getResourceName();
        Logger.log(LABEL_PROCESSING_RESOURCE);        } */
}

//
// Remove Processing label
//
function cleanup() {
    //Logger.log('Cleanup()');

    var cleanupList = [AdsApp.adGroups(), AdsApp.shoppingAdGroups()];

    for (i = 0; i < cleanupList.length; i++) {
      var iterator = cleanupList[i].withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']").get();

      while (iterator.hasNext()) {
        iterator.next().removeLabel(LABEL_PROCESSING);
      }
    }
}

//
// Set Device Bids
//
function setDeviceBidModifier(dateRange, dateRangeEnd) {
    // Logger.log('SetDeviceBidModifier');
    // TODO: Don't need to separate by destop/tablet/mobile. Just loop through them all and make adjustments.
    var STANDARD = 0;
    var SHOPPING = 1;

    Logger.log('Date Range from ' + dateRange + ' to ' + dateRangeEnd);

    for (i = 0; i < 2; i++) {
        Logger.log('---  ' + (i==STANDARD ? 'Standard Campaigns' : 'Shopping Campaigns'));

        // Logger.log("campaign.labels CONTAINS ANY ('" + LABEL_PROCESSING[l].resourceName + "')"); // Beta
        //Logger.log("campaign.labels CONTAINS ANY ('" + LABEL_PROCESSING + "')");

        var campaigns = (i==STANDARD ? AdsApp.campaigns() : AdsApp.shoppingCampaigns());
        var campaignIterator = campaigns.forDateRange(dateRange, dateRangeEnd)
            //.withCondition("campaign.status = ENABLED") // Beta Scripts
            //.withCondition("metrics.conversions > " + (MIN_CONVERSIONS-1))
            //.withCondition("campaign.labels CONTAINS ANY ('" + LABEL_PROCESSING_RESOURCE + "')") // Beta Scripts
            .withCondition("Status = ENABLED") // Old Scripts
            .withCondition("Conversions > " + (MIN_CONVERSIONS-1))
            .withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']") // Old Scripts

            .get();

        while (campaignIterator.hasNext()) {
            var campaign = campaignIterator.next();
            var baseConversionRate = campaign.getStatsFor(dateRange, dateRangeEnd).getConversionRate();
            var platforms = [campaign.targeting().platforms().desktop(),
                campaign.targeting().platforms().mobile(),
                campaign.targeting().platforms().tablet()
            ];

            // Logger.log('    CAMPAIGN: ' + campaign.getName());

            for (l = 0; l < platforms.length; l++) {
                var targetIterator = platforms[l].get();
                if (targetIterator.hasNext()) {
                    var target = targetIterator.next();
                    var stats = target.getStatsFor(dateRange, dateRangeEnd);
                    var conversions = stats.getConversions();
                    var conversionRate = stats.getConversionRate();
                    var targetModifier = (conversionRate / baseConversionRate);
                    var currentModifier = target.getBidModifier();

                    // Logger.log('    Conversions: ' + conversions);

                    // TODO: If inusfficient conversions but high cost, adjust anyway
                    if (conversions >= MIN_CONVERSIONS) {
                        if (Math.abs(currentModifier - targetModifier) >= BID_INCREMENT) {
                            if (targetModifier > currentModifier) {
                                target.setBidModifier(Math.min(currentModifier + BID_INCREMENT, MAX_BID_ADJUSTMENT));
                            } else {
                                target.setBidModifier(Math.max(currentModifier - BID_INCREMENT, 0.1));
                            }
                        }

                        campaign.removeLabel(LABEL_PROCESSING);
                        //Logger.log('    Remove Label: ' + LABEL_PROCESSING);
                    }
                }
            }

        }
    }
}

//
// Date range helper function
// Returns today's date
//
function TODAY() {
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth() + 1; // 0-11
    var yyyy = today.getFullYear();

    return { year: yyyy, month: mm, day: dd };
}

//
// Date range helper functions
// Returns date 90 days ago
//
function LAST_90_DAYS() {
    var date = new Date();
    date.setDate(date.getDate() - 90);

    var dd = date.getDate();
    var mm = date.getMonth()+1; // 0-11
    var yyyy = date.getFullYear();

    return {year: yyyy, month: mm, day: dd};
  }

//
// Date range helper functions
// Returns date 1 year ago
//
function LAST_YEAR() {
    var today = TODAY();

    today.year = today.year - 1;
    return today;
}
<commit_msg>tweaks<commit_after>// Version: 2.1
// Latest Source: https://github.com/Czarto/Adwords-Scripts/blob/master/device-bid-adjustments.js
//
// This Google Ads Script will incrementally change device bid adjustments
// based on conversion rates using the Campaign's average conversion rate
// as a baseline.
//

/***********

MIT License

Copyright (c) 2016-2021 Alex Czartoryski

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

**********/

var LABEL_PROCESSING = "_processing_device";
var LABEL_PROCESSING_RESOURCE = "";

var BID_INCREMENT = 0.05;       // Value by which to adjust bids
var MIN_CONVERSIONS = 10;       // Minimum conversions needed to adjust bids.
var MAX_BID_ADJUSTMENT = 1.90;  // Do not increase adjustments above this value

function main() {
    initLabels(); // Create Labels

    /*****
      Device performance *should* theoretically not vary over time
      (unless a site redesign has been performed) and so it makes
      most sense to use a relatively long time period (1 year)
      on which to base adjustments.

      Shorter time periods included for reference, but commented out
    *****/

    //setDeviceBidModifier("LAST_7_DAYS");
    //setDeviceBidModifier("LAST_14_DAYS");
    //setDeviceBidModifier("LAST_30_DAYS");
    //setDeviceBidModifier(LAST_90_DAYS(), TODAY());
    setDeviceBidModifier(LAST_YEAR(), TODAY());
    setDeviceBidModifier("ALL_TIME");

    cleanup(); // Remove Labels
}

//
// Set the Processing label
// This keeps track of which bid adjustments have already been processed
// in the case where multiple time-lookback windows are being used
//
function initLabels() {
    checkLabelExists();
    cleanup();

    var itemsToLabel = [AdsApp.campaigns(), AdsApp.shoppingCampaigns()];

    for (i = 0; i < itemsToLabel.length; i++) {
        var iterator = itemsToLabel[i].withCondition("Status = ENABLED").get();

        while (iterator.hasNext()) {
            iterator.next().applyLabel(LABEL_PROCESSING);
        }
    }
}

//
// Create the processing label if it does not exist
//
function checkLabelExists() {
    var labelIterator = AdsApp.labels().withCondition("Name = '" + LABEL_PROCESSING + "'" ).get();

    if( !labelIterator.hasNext()) {
        AdsApp.createLabel(LABEL_PROCESSING, "AdWords Scripts label used to process bids")
    }

    // Get label resource Ids (for beta experience script)
    /* Beta
    var labelIterator = AdsApp.labels().withCondition("Name = '" + LABEL_PROCESSING "'").get();
    if (labelIterator.hasNext()) {
        LABEL_PROCESSING_RESOURCE = labelIterator.next().getResourceName();
        Logger.log(LABEL_PROCESSING_RESOURCE);        } */
}

//
// Remove Processing label
//
function cleanup() {
    var cleanupList = [AdsApp.adGroups(), AdsApp.shoppingAdGroups()];

    for (i = 0; i < cleanupList.length; i++) {
      var iterator = cleanupList[i].withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']").get();

      while (iterator.hasNext()) {
        iterator.next().removeLabel(LABEL_PROCESSING);
      }
    }
}

//
// Set Device Bids
//
function setDeviceBidModifier(dateRange, dateRangeEnd) {
    var STANDARD = 0;
    var SHOPPING = 1;

    Logger.log('Date Range from ' + dateRange + ' to ' + dateRangeEnd);

    for (i = 0; i < 2; i++) {
        Logger.log('---  ' + (i==STANDARD ? 'Standard Campaigns' : 'Shopping Campaigns'));

        var campaigns = (i==STANDARD ? AdsApp.campaigns() : AdsApp.shoppingCampaigns());
        var campaignIterator = campaigns.forDateRange(dateRange, dateRangeEnd)
            //.withCondition("campaign.status = ENABLED") // Beta Scripts
            //.withCondition("metrics.conversions > " + (MIN_CONVERSIONS-1))
            //.withCondition("campaign.labels CONTAINS ANY ('" + LABEL_PROCESSING_RESOURCE + "')") // Beta Scripts
            .withCondition("Status = ENABLED") // Old Scripts
            .withCondition("Conversions > " + (MIN_CONVERSIONS-1))
            .withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']") // Old Scripts
            .get();

        while (campaignIterator.hasNext()) {
            var campaign = campaignIterator.next();
            var baseConversionRate = campaign.getStatsFor(dateRange, dateRangeEnd).getConversionRate();
            var platforms = [campaign.targeting().platforms().desktop(),
                campaign.targeting().platforms().mobile(),
                campaign.targeting().platforms().tablet()
            ];

            for (l = 0; l < platforms.length; l++) {
                var targetIterator = platforms[l].get();
                if (targetIterator.hasNext()) {
                    var target = targetIterator.next();
                    var stats = target.getStatsFor(dateRange, dateRangeEnd);
                    var conversions = stats.getConversions();
                    var conversionRate = stats.getConversionRate();
                    var targetModifier = (conversionRate / baseConversionRate);
                    var currentModifier = target.getBidModifier();

                    // TODO: If inusfficient conversions but high cost, adjust anyway
                    if (conversions >= MIN_CONVERSIONS) {
                        if (Math.abs(currentModifier - targetModifier) >= BID_INCREMENT) {
                            if (targetModifier > currentModifier) {
                                target.setBidModifier(Math.min(currentModifier + BID_INCREMENT, MAX_BID_ADJUSTMENT));
                            } else {
                                target.setBidModifier(Math.max(currentModifier - BID_INCREMENT, 0.1));
                            }
                        }

                        campaign.removeLabel(LABEL_PROCESSING);
                    }
                }
            }

        }
    }
}

//
// Date range helper function
// Returns today's date
//
function TODAY() {
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth() + 1; // 0-11
    var yyyy = today.getFullYear();

    return { year: yyyy, month: mm, day: dd };
}

//
// Date range helper functions
// Returns date 90 days ago
//
function LAST_90_DAYS() {
    var date = new Date();
    date.setDate(date.getDate() - 90);

    var dd = date.getDate();
    var mm = date.getMonth()+1; // 0-11
    var yyyy = date.getFullYear();

    return {year: yyyy, month: mm, day: dd};
  }

//
// Date range helper functions
// Returns date 1 year ago
//
function LAST_YEAR() {
    var today = TODAY();

    today.year = today.year - 1;
    return today;
}
<|endoftext|>


<commit_before>var app = app || {};

app.mapui = (function() {

    var el = null;

    function initCollapsible() {
        $('.collapsible').collapsible({
            accordion: true // A setting that changes the collapsible behavior to expandable instead of the default accordion style
        });
    };

    function initSideNav() {
        $(".button-collapse").sideNav({
            menuWidth: 300, // Default is 240
            edge: 'right', // Choose the horizontal origin
            closeOnClick: true // Closes side-nav on <a> clicks, useful for Angular/Meteor
        });
    }

    function initDropDown() {
        $('.dropdown-button').dropdown({
            inDuration: 300,
            outDuration: 225,
            constrain_width: true, // Does not change width of dropdown to that of the activator
            hover: false, // Activate on hover
            gutter: 0, // Spacing from edge
            belowOrigin: true, // Displays dropdown below the button
            alignment: 'left' // Displays dropdown with edge aligned to the left of button
        });
    };

    function initExpander() {
        $('.expander').click(function() {
            $('.sidebar').toggleClass('active');
            $('.expander').toggleClass('active');
        });
    };

    function initClimateToolsNavigation() {
        $('.climate-tool-button').click(function() {
            $('.climate-tools, .climate-tool-button').toggleClass("active");
        });
    }

    function initMaterialDesignSelection() {
        // init the materialize selection
        $('select').material_select();
    }

    function initWithMapDescription() {
        // init the materialize selection
        $('.map-description').click();
        // $('.scatter-panel').click();
    }

    function initWithToolbox(){
        $('.climate-tool-button').click();
    }

    function initSlider() {
        // range slider
        var slider = document.getElementById('range-input');
        noUiSlider.create(slider, {
            start: [2000, 2013],
            connect: true,
            step: 1,
            range: {
                'min': 1901,
                'max': 2100
            }
        });
    }

    function activateMapDisplayButtons() {
        $('.map-display-buttons').click(function() {
            console.log($(this).text().toUpperCase().trim());
            $('.map-display-buttons').addClass("disabled");
            $(this).toggleClass("disabled");
            if ($(this).text().toUpperCase().trim() == "CLIMATE") {
                $('.climate-tools, .climate-tool-button').addClass("active");
            } else {
                $('.climate-tools, .climate-tool-button').addClass("active");
            };
        });
    }

    function populate(selector, objlist) {
        objlist.forEach(function(d, i) {
            if (i == 0) {
                $(selector)
                    .append('<option value="' + d + '" selected>' + d + '</option>')
            } else {
                $(selector)
                    .append('<option value="' + d + '">' + d + '</option>')
            }
        })
    }

    function feedBecUnitSelector(){
        var query = "SELECT DISTINCT map_label FROM " + el.dataset_selected + " WHERE map_label IS NOT NULL";
        $.getJSON('https://becexplorer.cartodb.com/api/v2/sql?q=' + query, function(data) {
            data.rows.forEach(function(d){
                el.bec_names.push(d.map_label);
            })
            $('.bec-unit-variables select').children().remove().end();
            populate('.bec-unit-variables select', el.bec_names);

             // console.log($('.bec-focal-selector :selected').text());
            el.focal_name =  $('.bec-focal-selector :selected').text();
            el.comparison_name =  $('.bec-comparison-selector :selected').text();
            // make sure that the material select is called to update the dropdown
            $("select").material_select();
        });

    }

    function setClimateSelected(){
        el.climate_selected = $('.climate-variables-map :selected:first').val();
        console.log(el.climate_selected);

        console.log("setClimateSelected - mapui is:", el.climate_selected);
        $("select").material_select();
    }

    function colorMapByClimate(){
        $('.climate-variables-button, .update-climate-map').click(function(){
            setClimateSelected();
            var query = 'SELECT ' + el.climate_selected + ', cartodb_id FROM ' + el.dataset_selected;

            // NEEED TO FIND A GOOD WAY TO CALCULATE BREAKS
            // THEN STYLE THE VALUES BASED ON THE BREAKS WITH CONDITIONALS!!!
            $.getJSON('https://'+el.username+'.cartodb.com/api/v2/sql/?q='+query, function(data) {
                var max = d3.max(data.rows, function(d){
                    return d[el.climate_selected];
                })
                var min = d3.min(data.rows, function(d){
                    return d[el.climate_selected];
                })

                var quantize = d3.scale.quantize()
                  .domain([min, max])
                  .range([min, max]);

                var color = d3.scale.quantize()
                    .domain([min, max])
                    .range(colorbrewer.GnBu[9]);

                var dom = color.domain(),
                    l = (dom[1] - dom[0])/color.range().length,
                    breaks = d3.range(0, color.range().length).map(function(i) { return i * l; });
                    breaks = breaks.reverse();
                    // console.log(breaks);

                 var styleArray = [];

                 breaks.forEach(function(d, i){
                    var output;
                    output = '#' + el.dataset_selected + '['+ el.climate_selected + ' <= ' + d + ']{polygon-fill:' + color(d) + '}';
                    styleArray.push(output)
                 })
                 styleArray.unshift('#' + el.dataset_selected + '['+ el.climate_selected + ' < ' + max+ ']{polygon-fill:' + color(max) + '}');

                // set the color
                el.bec_cartocss[el.climate_selected] = null;
                el.bec_cartocss[el.climate_selected] = styleArray.join("\n");
                el.data_layer.setCartoCSS(el.bec_cartocss[el.climate_selected]);
            });
        });
    }

    // super hacky way to recolor map - take care of this later by
    // refactoring the colorMapByClimate() function - but in the end it kind of works nicely
    // since it fires the "climate" button and updates the map at the same time.
    function updateClimateMap(){
        $('.climate-variables-map').change(function(){
            $('.climate-variables-button').click();
        });
    }

    function updateTimeScaleSelected(){
        $('.timescale-selector select').change(function(){
            el.timescale_selected = $(".timescale-selector select").val();

                if (el.timescale_selected == "all"){
                    $('.climate-variables select').children().remove().end();
                    populate('.climate-variables select', el.column_names);
                } else if (el.timescale_selected == "monthly"){
                    $('.climate-variables select').children().remove().end();
                    populate('.climate-variables select', el.monthly_columns);
                } else if (el.timescale_selected == "seasonal"){
                    $('.climate-variables select').children().remove().end();
                    populate('.climate-variables select', el.seasonal_columns);
                } else if (el.timescale_selected == "annual"){
                    $('.climate-variables select').children().remove().end();
                    populate('.climate-variables select', el.annual_columns);
                }

            $('select').material_select();
        });
    }

    // this is a temporary fix!!! on page load make sure the variables shown are same as what is loaded async
    // with the cliamte variables
    function setInitalClimateVariable() {
        $('.climate-variables :selected').val('rh_wt');
        $("select").material_select();
    }

    var init = function() {
        el = app.main.el;
        initCollapsible();
        initDropDown();
        initExpander();
        initClimateToolsNavigation();
        initWithMapDescription();
        // initSlider();
        activateMapDisplayButtons();
        feedBecUnitSelector();
        colorMapByClimate();
        updateClimateMap();
        updateTimeScaleSelected();
        initWithToolbox();
         setInitalClimateVariable();
        // initMaterialDesignSelection(); // need to call this after getAllClimateVariables to update the form

        // initSideNav(); // this is done in the main layout
    };

    return {
        init: init
    }
})();
<commit_msg>moved related functions to map to mapui<commit_after>var app = app || {};

app.mapui = (function() {

    var el = null;

    // change the map display based on the selecte button
    function changeMapDisplay(){
        $('.map-display-buttons').click(function() {
            // disable the selected button color
            $('.map-display-buttons a').addClass('disabled');
            // get the name of the selected feature
            var sel = $(this).text().toUpperCase().trim();
            // turn on the selected button color
            $('a', this).toggleClass('disabled');
            console.log("the map option selected: ", sel);
            // change the map based on the button selected
            if (sel == "CLIMATE") {
                el.data_layer.setCartoCSS(el.bec_cartocss[el.selected_unit]);
            } else if (sel == "BEC UNIT") {
                el.data_layer.setCartoCSS(el.bec_cartocss.unit);
            } else if (sel == "BEC ZONE") {
                el.data_layer.setCartoCSS(el.bec_cartocss.zone);
            };
        });
    };

    // NOT CURRENTLY USED IN APP
    function initSlider() {
        // range slider
        var slider = document.getElementById('range-input');
        noUiSlider.create(slider, {
            start: [2000, 2013],
            connect: true,
            step: 1,
            range: {
                'min': 1901,
                'max': 2100
            }
        });
    }

    // set the climate variable to the first feature
    function setClimateSelected(){
        el.climate_selected = $('.climate-variables-map :selected:first').val();
        console.log("setClimateSelected - mapui is:", el.climate_selected);
        $("select").material_select();
    }

    function colorMapByClimate(){
        $('.climate-variables-button, .update-climate-map').click(function(){
            setClimateSelected();
            var query = 'SELECT ' + el.climate_selected + ', cartodb_id FROM ' + el.dataset_selected;

            // NEEED TO FIND A GOOD WAY TO CALCULATE BREAKS
            // THEN STYLE THE VALUES BASED ON THE BREAKS WITH CONDITIONALS!!!
            $.getJSON('https://'+el.username+'.cartodb.com/api/v2/sql/?q='+query, function(data) {
                var max = d3.max(data.rows, function(d){
                    return d[el.climate_selected];
                })
                var min = d3.min(data.rows, function(d){
                    return d[el.climate_selected];
                })

                var quantize = d3.scale.quantize()
                  .domain([min, max])
                  .range([min, max]);

                var color = d3.scale.quantize()
                    .domain([min, max])
                    .range(colorbrewer.GnBu[9]);

                var dom = color.domain(),
                    l = (dom[1] - dom[0])/color.range().length,
                    breaks = d3.range(0, color.range().length).map(function(i) { return i * l; });
                    breaks = breaks.reverse();
                    // console.log(breaks);

                 var styleArray = [];

                 breaks.forEach(function(d, i){
                    var output;
                    output = '#' + el.dataset_selected + '['+ el.climate_selected + ' <= ' + d + ']{polygon-fill:' + color(d) + '}';
                    styleArray.push(output)
                 })
                 styleArray.unshift('#' + el.dataset_selected + '['+ el.climate_selected + ' < ' + max+ ']{polygon-fill:' + color(max) + '}');

                // set the color
                el.bec_cartocss[el.climate_selected] = null;
                el.bec_cartocss[el.climate_selected] = styleArray.join("\n");
                el.data_layer.setCartoCSS(el.bec_cartocss[el.climate_selected]);
            });
        });
    }

    // super hacky way to recolor map - take care of this later by
    // refactoring the colorMapByClimate() function - but in the end it kind of works nicely
    // since it fires the "climate" button and updates the map at the same time.
    function updateClimateMap(){
        $('.climate-variables-map').change(function(){
            $('.climate-variables-button').click();
        });
    }

    // this is a temporary fix!!! on page load make sure the variables shown are same as what is loaded async
    // with the cliamte variables
    function setInitalClimateVariable() {
        $('.climate-variables :selected').val('rh_wt');
        $("select").material_select();
    }

    var init = function() {
        el = app.main.el;
        changeMapDisplay();
        colorMapByClimate();
        updateClimateMap();
        setInitalClimateVariable();
        // initSlider();
    };

    return {
        init: init
    }
})();
<|endoftext|>


<commit_before>import {
	BufferGeometry,
	Color,
	Float32BufferAttribute,
	Vector3
} from '../../../build/three.module.js';

/**
 * Break faces with edges longer than maxEdgeLength
 */

var TessellateModifier = function ( maxEdgeLength = 0.1, maxIterations = 6 ) {

	this.maxEdgeLength = maxEdgeLength;
	this.maxIterations = maxIterations;

};

TessellateModifier.prototype.modify = function ( geometry ) {

	if ( geometry.isBufferGeometry !== true ) {

		console.warn( 'TessellateModifier: geometry is not a BufferGeometry.', geometry );
		return geometry;

	}

	//

	const maxIterations = this.maxIterations;
	const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;

	const va = new Vector3();
	const vb = new Vector3();
	const vc = new Vector3();
	const vm = new Vector3();
	const vs = [ va, vb, vc, vm ];

	const na = new Vector3();
	const nb = new Vector3();
	const nc = new Vector3();
	const nm = new Vector3();
	const ns = [ na, nb, nc, nm ];

	const ca = new Color();
	const cb = new Color();
	const cc = new Color();
	const cm = new Color();
	const cs = [ ca, cb, cc, cm ];

	const attributes = geometry.attributes;
	const hasNormals = attributes.normal !== undefined;
	const hasColors = attributes.color !== undefined;

	let positions = attributes.position.array;
	let normals = hasNormals ? attributes.normal.array : null;
	let colors = hasColors ? attributes.color.array : null;

	let positions2 = positions;
	let normals2 = normals;
	let colors2 = colors;

	let iteration = 0;
	let tessellating = true;

	function addTriangle( a, b, c ) {

		const v1 = vs[ a ];
		const v2 = vs[ b ];
		const v3 = vs[ c ];

		positions2.push( v1.x, v1.y, v1.z );
		positions2.push( v2.x, v2.y, v2.z );
		positions2.push( v3.x, v3.y, v3.z );

		if ( hasNormals ) {

			const n1 = ns[ a ];
			const n2 = ns[ b ];
			const n3 = ns[ c ];

			normals2.push( n1.x, n1.y, n1.z );
			normals2.push( n2.x, n2.y, n2.z );
			normals2.push( n3.x, n3.y, n3.z );

		}

		if ( hasColors ) {

			const c1 = cs[ a ];
			const c2 = cs[ b ];
			const c3 = cs[ c ];

			colors2.push( c1.x, c1.y, c1.z );
			colors2.push( c2.x, c2.y, c2.z );
			colors2.push( c3.x, c3.y, c3.z );

		}

	}

	while ( tessellating && iteration < maxIterations ) {

		iteration ++;
		tessellating = false;

		positions = positions2;
		positions2 = [];

		if ( hasNormals ) {

			normals = normals2;
			normals2 = [];

		}

		if ( hasColors ) {

			colors = colors2;
			colors2 = [];

		}

		for ( var i = 0, il = positions.length; i < il; i += 9 ) {

			va.fromArray( positions, i + 0 );
			vb.fromArray( positions, i + 3 );
			vc.fromArray( positions, i + 6 );

			if ( hasNormals ) {

				na.fromArray( normals, i + 0 );
				nb.fromArray( normals, i + 3 );
				nc.fromArray( normals, i + 6 );

			}

			if ( hasColors ) {

				ca.fromArray( colors, i + 0 );
				cb.fromArray( colors, i + 3 );
				cc.fromArray( colors, i + 6 );

			}

			const dab = va.distanceToSquared( vb );
			const dbc = vb.distanceToSquared( vc );
			const dac = va.distanceToSquared( vc );

			if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {

				tessellating = true;

				if ( dab >= dbc && dab >= dac ) {

					vm.lerpVectors( va, vb, 0.5 );
					if ( hasNormals ) nm.lerpVectors( na, nb, 0.5 );
					if ( hasColors ) cm.lerpColors( ca, cb, 0.5 );

					addTriangle( 0, 3, 2 );
					addTriangle( 3, 1, 2 );

				} else if ( dbc >= dab && dbc >= dac ) {

					vm.lerpVectors( vb, vc, 0.5 );
					if ( hasNormals ) nm.lerpVectors( nb, nc, 0.5 );
					if ( hasColors ) cm.lerpColors( cb, cc, 0.5 );

					addTriangle( 0, 1, 3 );
					addTriangle( 3, 2, 0 );

				} else {

					vm.lerpVectors( va, vc, 0.5 );
					if ( hasNormals ) nm.lerpVectors( na, nc, 0.5 );
					if ( hasColors ) cm.lerpColors( ca, cc, 0.5 );

					addTriangle( 0, 1, 3 );
					addTriangle( 3, 1, 2 );

				}

			} else {

				addTriangle( 0, 1, 2 );

			}

		}

	}

	const geometry2 = new BufferGeometry();

	geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );

	if ( hasNormals ) {

		geometry2.setAttribute( 'normal', new Float32BufferAttribute( normals2, 3 ) );

	}

	if ( hasColors ) {

		geometry2.setAttribute( 'color', new Float32BufferAttribute( colors2, 3 ) );

	}

	return geometry2;

};

export { TessellateModifier };
<commit_msg>Updated modules.<commit_after>import {
	BufferGeometry,
	Color,
	Float32BufferAttribute,
	Geometry,
	Vector2,
	Vector3
} from '../../../build/three.module.js';

/**
 * Break faces with edges longer than maxEdgeLength
 */

var TessellateModifier = function ( maxEdgeLength = 0.1, maxIterations = 6 ) {

	this.maxEdgeLength = maxEdgeLength;
	this.maxIterations = maxIterations;

};

TessellateModifier.prototype.modify = function ( geometry ) {

	if ( geometry.isGeometry === true ) {

		console.error( 'THREE.TessellateModifier no longer supports Geometry. Use BufferGeometry instead.' );
		return geometry;

	}

	if ( geometry.index !== null ) {

		geometry = geometry.toNonIndexed();

	}

	//

	const maxIterations = this.maxIterations;
	const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;

	const va = new Vector3();
	const vb = new Vector3();
	const vc = new Vector3();
	const vm = new Vector3();
	const vs = [ va, vb, vc, vm ];

	const na = new Vector3();
	const nb = new Vector3();
	const nc = new Vector3();
	const nm = new Vector3();
	const ns = [ na, nb, nc, nm ];

	const ca = new Color();
	const cb = new Color();
	const cc = new Color();
	const cm = new Color();
	const cs = [ ca, cb, cc, cm ];

	const ua = new Vector2();
	const ub = new Vector2();
	const uc = new Vector2();
	const um = new Vector2();
	const us = [ ua, ub, uc, um ];

	const u2a = new Vector2();
	const u2b = new Vector2();
	const u2c = new Vector2();
	const u2m = new Vector2();
	const u2s = [ u2a, u2b, u2c, u2m ];

	const attributes = geometry.attributes;
	const hasNormals = attributes.normal !== undefined;
	const hasColors = attributes.color !== undefined;
	const hasUVs = attributes.uv !== undefined;
	const hasUV2s = attributes.uv2 !== undefined;

	let positions = attributes.position.array;
	let normals = hasNormals ? attributes.normal.array : null;
	let colors = hasColors ? attributes.color.array : null;
	let uvs = hasUVs ? attributes.uv.array : null;
	let uv2s = hasUV2s ? attributes.uv2.array : null;

	let positions2 = positions;
	let normals2 = normals;
	let colors2 = colors;
	let uvs2 = uvs;
	let uv2s2 = uv2s;

	let iteration = 0;
	let tessellating = true;

	function addTriangle( a, b, c ) {

		const v1 = vs[ a ];
		const v2 = vs[ b ];
		const v3 = vs[ c ];

		positions2.push( v1.x, v1.y, v1.z );
		positions2.push( v2.x, v2.y, v2.z );
		positions2.push( v3.x, v3.y, v3.z );

		if ( hasNormals ) {

			const n1 = ns[ a ];
			const n2 = ns[ b ];
			const n3 = ns[ c ];

			normals2.push( n1.x, n1.y, n1.z );
			normals2.push( n2.x, n2.y, n2.z );
			normals2.push( n3.x, n3.y, n3.z );

		}

		if ( hasColors ) {

			const c1 = cs[ a ];
			const c2 = cs[ b ];
			const c3 = cs[ c ];

			colors2.push( c1.x, c1.y, c1.z );
			colors2.push( c2.x, c2.y, c2.z );
			colors2.push( c3.x, c3.y, c3.z );

		}

		if ( hasUVs ) {

			const u1 = us[ a ];
			const u2 = us[ b ];
			const u3 = us[ c ];

			uvs2.push( u1.x, u1.y );
			uvs2.push( u2.x, u2.y );
			uvs2.push( u3.x, u3.y );

		}

		if ( hasUV2s ) {

			const u21 = u2s[ a ];
			const u22 = u2s[ b ];
			const u23 = u2s[ c ];

			uv2s2.push( u21.x, u21.y );
			uv2s2.push( u22.x, u22.y );
			uv2s2.push( u23.x, u23.y );

		}

	}

	while ( tessellating && iteration < maxIterations ) {

		iteration ++;
		tessellating = false;

		positions = positions2;
		positions2 = [];

		if ( hasNormals ) {

			normals = normals2;
			normals2 = [];

		}

		if ( hasColors ) {

			colors = colors2;
			colors2 = [];

		}

		if ( hasUVs ) {

			uvs = uvs2;
			uvs2 = [];

		}

		if ( hasUV2s ) {

			uv2s = uv2s2;
			uv2s2 = [];

		}

		for ( var i = 0, i2 = 0, il = positions.length; i < il; i += 9, i2 += 6 ) {

			va.fromArray( positions, i + 0 );
			vb.fromArray( positions, i + 3 );
			vc.fromArray( positions, i + 6 );

			if ( hasNormals ) {

				na.fromArray( normals, i + 0 );
				nb.fromArray( normals, i + 3 );
				nc.fromArray( normals, i + 6 );

			}

			if ( hasColors ) {

				ca.fromArray( colors, i + 0 );
				cb.fromArray( colors, i + 3 );
				cc.fromArray( colors, i + 6 );

			}

			if ( hasUVs ) {

				ua.fromArray( uvs, i2 + 0 );
				ub.fromArray( uvs, i2 + 2 );
				uc.fromArray( uvs, i2 + 4 );

			}

			if ( hasUV2s ) {

				u2a.fromArray( uv2s, i2 + 0 );
				u2b.fromArray( uv2s, i2 + 2 );
				u2c.fromArray( uv2s, i2 + 4 );

			}

			const dab = va.distanceToSquared( vb );
			const dbc = vb.distanceToSquared( vc );
			const dac = va.distanceToSquared( vc );

			if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {

				tessellating = true;

				if ( dab >= dbc && dab >= dac ) {

					vm.lerpVectors( va, vb, 0.5 );
					if ( hasNormals ) nm.lerpVectors( na, nb, 0.5 );
					if ( hasColors ) cm.lerpColors( ca, cb, 0.5 );
					if ( hasUVs ) um.lerpVectors( ua, ub, 0.5 );
					if ( hasUV2s ) u2m.lerpVectors( u2a, u2b, 0.5 );

					addTriangle( 0, 3, 2 );
					addTriangle( 3, 1, 2 );

				} else if ( dbc >= dab && dbc >= dac ) {

					vm.lerpVectors( vb, vc, 0.5 );
					if ( hasNormals ) nm.lerpVectors( nb, nc, 0.5 );
					if ( hasColors ) cm.lerpColors( cb, cc, 0.5 );
					if ( hasUVs ) um.lerpVectors( ub, uc, 0.5 );
					if ( hasUV2s ) u2m.lerpVectors( u2b, u2c, 0.5 );

					addTriangle( 0, 1, 3 );
					addTriangle( 3, 2, 0 );

				} else {

					vm.lerpVectors( va, vc, 0.5 );
					if ( hasNormals ) nm.lerpVectors( na, nc, 0.5 );
					if ( hasColors ) cm.lerpColors( ca, cc, 0.5 );
					if ( hasUVs ) um.lerpVectors( ua, uc, 0.5 );
					if ( hasUV2s ) u2m.lerpVectors( u2a, u2c, 0.5 );

					addTriangle( 0, 1, 3 );
					addTriangle( 3, 1, 2 );

				}

			} else {

				addTriangle( 0, 1, 2 );

			}

		}

	}

	const geometry2 = new BufferGeometry();

	geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );

	if ( hasNormals ) {

		geometry2.setAttribute( 'normal', new Float32BufferAttribute( normals2, 3 ) );

	}

	if ( hasColors ) {

		geometry2.setAttribute( 'color', new Float32BufferAttribute( colors2, 3 ) );

	}

	if ( hasUVs ) {

		geometry2.setAttribute( 'uv', new Float32BufferAttribute( uvs2, 2 ) );

	}

	if ( hasUV2s ) {

		geometry2.setAttribute( 'uv2', new Float32BufferAttribute( uv2s2, 2 ) );

	}

	return geometry2;

};

export { TessellateModifier };
<|endoftext|>


<commit_before>$(document).ready(
// Test Comment Try 3
	function(){
			var test = 0; // pointless variable for testing please delete
			// Create a new oCanvas object
			var canvas = oCanvas.create({canvas: "#profileCanvas"});
			canvas.width  = window.innerWidth;

			// Global variables for points
			var pointCounter = 0;
			var pointArray = [];
			var bezier;

			// Create an audio context
			var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

			// New bezier drawing function
			function bezierCurve2(ctrlPoints, numPoints) {

				var rtnPoints = [];

				// At least four points are neccesary for a bezier
				if (ctrlPoints.length > 3) {

					// First curve is a special case where the last point is the midpoint between point 3 and point 4
					var midx = (ctrlPoints[2].x + ctrlPoints[3].x) / 2
					var midy = (ctrlPoints[2].y + ctrlPoints[3].y) / 2
					var n = numPoints / (ctrlPoints.length / 4);
					for (t = 0; t <= 1; t+=(1/n)) {
					  	x = Math.pow(1 - t, 3)*ctrlPoints[0].x + 3*Math.pow(1 -  t,2)*t*ctrlPoints[1].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[2].x + Math.pow(t,3)*midx;
					  	y = Math.pow(1 - t, 3)*ctrlPoints[0].y + 3*Math.pow(1 -  t,2)*t*ctrlPoints[1].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[2].y + Math.pow(t,3)*midy;
					  	rtnPoints[rtnPoints.length] = {x:x, y:y};
					}

					var lastmidx;
					var lastmidy;
					// the remainder of the curves are drawn from from prior curves midpoint to the next curves midpoint thus only using three points
					for (i = 3; i < ctrlPoints.length - 2; i+=2)
					{
						lastmidx = midx;
						lastmidy = midy;
					      	var midx = (ctrlPoints[i+1].x + ctrlPoints[i+2].x) / 2
					      	var midy = (ctrlPoints[i+1].y + ctrlPoints[i+2].y) / 2
					      	var n = numPoints / (ctrlPoints.length / 4);

					      	for (t = 0; t <= 1; t+=(1/n)) {
						  	x = Math.pow(1 - t, 3)*lastmidx + 3*Math.pow(1 -  t,2)*t*ctrlPoints[i].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[i+1].x + Math.pow(t,3)*midx;
						  	y = Math.pow(1 - t, 3)*lastmidy + 3*Math.pow(1 -  t,2)*t*ctrlPoints[i].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[i+1].y + Math.pow(t,3)*midy;
						  	rtnPoints[rtnPoints.length] = {x:x, y:y};
						}
					}

				}

				// Once I have all the points for the bezier curve I need to loop through all of them looking for evenly spaced forward moving values of x

				// Im assuming that the first x minus the last x constitute the values of x I have to play with (this can be not true but is a necessary assumption)
				var len = Math.abs(ctrlPoints[ctrlPoints.length - 1].x - ctrlPoints[0].x)
				var incr = len / numPoints;
				var j = 0;

				var rtnDiscretePoints = [];
				var i = 1;
				while ( i < rtnPoints.length) {
					// if the smaller of the adjacent points x values is equal to j for then store it
					if(j == rtnPoints[i-1].x) {
						rtnDiscretePoints[rtnDiscretePoints.length] = {x:rtnPoints[i-1].x, y:rtnPoints[i-1].y};
						j+=incr;
					}
					// if j is between the two points then find j along the line between the two points and store the y value
					else if(j <= rtnPoints[i].x  && j > rtnPoints[i-1] < j) {
						// compute the slope
						var m = (rtnPoints[i].y - rtnPoints[i-1].y) / (rtnPoints[i].x - rtnPoints[i-1].x)
						// compute the value of y at x = j
						var y = m*(j - rtnPoints[i-1].x) + rtnPoints[i-1].y
						// store the point
						rtnDiscretePoints[rtnDiscretePoints.length] = {x:j, y:y};
						j+=incr;
					}

					// if j is smaller than the first points x value then increment j
					else if (j < rtnPoints[i-1].x) { j+=incr; }
					// if j is larger than the last points x value then increment i
					else if(j > rtnPoints[i].x) { i++; }

				}

				return rtnDiscretePoints;

			}

			// Register a new object for a curve
			canvas.display.register("bezierCurve", {shapeType: "bezierCurve", control: 1}, function (canvas) {

				if (this.strokeWidth > 0) {
					canvas.strokeStyle = this.strokeColor;
					canvas.lineWidth = this.strokeWidth;
				}

				// Draw lines for every other two control points if theres no lock
				canvas.beginPath();
		 		if (this.control == 1) {canvas.strokeStyle = "#FF0000";} else {canvas.strokeStyle = "#fff";}
		 		for (i = 0; i < this.points.length; i++){this.points[i].strokeColor = canvas.strokeStyle;}

		 		for (i = 2; i < this.points.length; i+=2)
				{
					if (this.control == 1) {
						canvas.moveTo(this.points[i-1].x, this.points[i-1].y);
						canvas.lineTo(this.points[i].x, this.points[i].y);
					}

				}
				canvas.stroke();
	 			canvas.closePath();

				c = bezierCurve2(this.points, $("#points").val() );

				canvas.beginPath();
				canvas.strokeStyle = "#FF0000";
				for (t = 0; t < c.length ; t++) {
					canvas.fillRect(c[t].x,c[t].y,1,1);
				}
				canvas.stroke();
		 		canvas.closePath();

			});

			// Pressing x while the canvas is selected adds a small draggable circle to the canvas
			// As well as a line connecting it to the previous circle or the mid point of the canvas if its the first click
			canvas.bind("dblclick", function (x) {

			        // Right button press
				if (x.which == 1) {
					// Detect if button press is within an existing circle
					var detectbit = 0;
					if (pointCounter > 0) {
						for (i = 0; i < pointArray.length; i++)
						{
							if ( Math.pow( canvas.mouse.x - pointArray[i].x, 2) + Math.pow( canvas.mouse.y - pointArray[i].y, 2) < Math.pow(pointArray[i].radius,2)) {
								detectbit = 1;
							}
						}
					}

					// If not in an existing circle then add a new circle
					if (detectbit == 0) {
						pointCounter++;
						// if this is the first small circle add an extra small circle at the midpoint of the start of the canvas
						if (pointCounter == 1) {
							var point = canvas.display.ellipse({x: 0, y: canvas.height / 2, radius: 5,stroke: "1px #FF0000"});
							canvas.addChild(point);
							pointArray[pointArray.length] = point;
							bezier = canvas.display.bezierCurve({points: pointArray, stroke: "1px #000", control: 1});
							canvas.addChild(bezier);
						}
		    				var point = canvas.display.ellipse({x: canvas.mouse.x, y: canvas.mouse.y, radius: 5,stroke: "1px #FF0000"});
		    				pointArray[pointArray.length] = point;
						canvas.addChild(point);
						var dragOptions = { changeZindex: false };
						point.dragAndDrop(dragOptions);
					}

				}

			});

			canvas.bind("keydown", function (x) {

				// Spacebar turns on and off the control points and lines of the bezier object
				if (x.which == 32) {
					if(bezier.control == 0) {
						bezier.control = 1;
						canvas.redraw();
					} else {
						bezier.control = 0;
						canvas.redraw();
					}
				}

				// the letter E creates an endpoint
				if (x.which == 69) {
					var point = canvas.display.ellipse({x: canvas.width, y: canvas.height / 2, radius: 5,stroke: "1px #FF0000"});
		    			pointArray[pointArray.length] = point;
					canvas.addChild(point);
				}

				// the letter C clears the canvas and resets the global variables
				if (x.which == 67) {
					canvas.children = [];
					canvas.clear();
					pointCounter = 0;
					pointArray = [];
					bezier = null;
				}

			});

			$( '#play' ).click ( function () {

			        var wave = [];

			        wave = bezierCurve2(pointArray, $("#points").val());

			        // Stereo
				var channels = 2;

				// Create an empty duration seconds stereo buffer at the
				// sample rate of the AudioContext
				var frameCount = audioCtx.sampleRate * $("#duration").val();

				var myArrayBuffer = audioCtx.createBuffer(channels, frameCount, audioCtx.sampleRate);

				// Fill the buffer with my invented sound and the complement
				for (var channel = 0; channel < channels; channel++) {
					// This gives us the actual array that contains the data
					var nowBuffering = myArrayBuffer.getChannelData(channel);

				// loop over the entire frame count
				for (var j = 0; j < Math.floor(frameCount / wave.length); j++)

					for (var i = 0; i < wave.length; i++) {
						nowBuffering[(j* wave.length) + i] = ((wave[i].y - (canvas.height / 2)) / canvas.height);
						nowBuffering[(j* wave.length) + wave.length + i] = (-1 * (wave[i].y - (canvas.height / 2)) / canvas.height);
					}

				}

				// Get an AudioBufferSourceNode
				// This is the AudioNode to use when we want to play an AudioBuffer
				var source = audioCtx.createBufferSource();

				// set the buffer in the AudioBufferSourceNode
				source.buffer = myArrayBuffer;

				// connect the AudioBufferSourceNode to the
				// destination so we can hear the sound
				source.connect(audioCtx.destination);

				// start the source playing
				source.start();

			});

	});
	<commit_msg>fourth try<commit_after>$(document).ready(
// Test Comment Try 4
	function(){
			var test = 0; // pointless variable for testing please delete
			// Create a new oCanvas object
			var canvas = oCanvas.create({canvas: "#profileCanvas"});
			canvas.width  = window.innerWidth;

			// Global variables for points
			var pointCounter = 0;
			var pointArray = [];
			var bezier;

			// Create an audio context
			var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

			// New bezier drawing function
			function bezierCurve2(ctrlPoints, numPoints) {

				var rtnPoints = [];

				// At least four points are neccesary for a bezier
				if (ctrlPoints.length > 3) {

					// First curve is a special case where the last point is the midpoint between point 3 and point 4
					var midx = (ctrlPoints[2].x + ctrlPoints[3].x) / 2
					var midy = (ctrlPoints[2].y + ctrlPoints[3].y) / 2
					var n = numPoints / (ctrlPoints.length / 4);
					for (t = 0; t <= 1; t+=(1/n)) {
					  	x = Math.pow(1 - t, 3)*ctrlPoints[0].x + 3*Math.pow(1 -  t,2)*t*ctrlPoints[1].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[2].x + Math.pow(t,3)*midx;
					  	y = Math.pow(1 - t, 3)*ctrlPoints[0].y + 3*Math.pow(1 -  t,2)*t*ctrlPoints[1].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[2].y + Math.pow(t,3)*midy;
					  	rtnPoints[rtnPoints.length] = {x:x, y:y};
					}

					var lastmidx;
					var lastmidy;
					// the remainder of the curves are drawn from from prior curves midpoint to the next curves midpoint thus only using three points
					for (i = 3; i < ctrlPoints.length - 2; i+=2)
					{
						lastmidx = midx;
						lastmidy = midy;
					      	var midx = (ctrlPoints[i+1].x + ctrlPoints[i+2].x) / 2
					      	var midy = (ctrlPoints[i+1].y + ctrlPoints[i+2].y) / 2
					      	var n = numPoints / (ctrlPoints.length / 4);

					      	for (t = 0; t <= 1; t+=(1/n)) {
						  	x = Math.pow(1 - t, 3)*lastmidx + 3*Math.pow(1 -  t,2)*t*ctrlPoints[i].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[i+1].x + Math.pow(t,3)*midx;
						  	y = Math.pow(1 - t, 3)*lastmidy + 3*Math.pow(1 -  t,2)*t*ctrlPoints[i].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPoints[i+1].y + Math.pow(t,3)*midy;
						  	rtnPoints[rtnPoints.length] = {x:x, y:y};
						}
					}

				}

				// Once I have all the points for the bezier curve I need to loop through all of them looking for evenly spaced forward moving values of x

				// Im assuming that the first x minus the last x constitute the values of x I have to play with (this can be not true but is a necessary assumption)
				var len = Math.abs(ctrlPoints[ctrlPoints.length - 1].x - ctrlPoints[0].x)
				var incr = len / numPoints;
				var j = 0;

				var rtnDiscretePoints = [];
				var i = 1;
				while ( i < rtnPoints.length) {
					// if the smaller of the adjacent points x values is equal to j for then store it
					if(j == rtnPoints[i-1].x) {
						rtnDiscretePoints[rtnDiscretePoints.length] = {x:rtnPoints[i-1].x, y:rtnPoints[i-1].y};
						j+=incr;
					}
					// if j is between the two points then find j along the line between the two points and store the y value
					else if(j <= rtnPoints[i].x  && j > rtnPoints[i-1] < j) {
						// compute the slope
						var m = (rtnPoints[i].y - rtnPoints[i-1].y) / (rtnPoints[i].x - rtnPoints[i-1].x)
						// compute the value of y at x = j
						var y = m*(j - rtnPoints[i-1].x) + rtnPoints[i-1].y
						// store the point
						rtnDiscretePoints[rtnDiscretePoints.length] = {x:j, y:y};
						j+=incr;
					}

					// if j is smaller than the first points x value then increment j
					else if (j < rtnPoints[i-1].x) { j+=incr; }
					// if j is larger than the last points x value then increment i
					else if(j > rtnPoints[i].x) { i++; }

				}

				return rtnDiscretePoints;

			}

			// Register a new object for a curve
			canvas.display.register("bezierCurve", {shapeType: "bezierCurve", control: 1}, function (canvas) {

				if (this.strokeWidth > 0) {
					canvas.strokeStyle = this.strokeColor;
					canvas.lineWidth = this.strokeWidth;
				}

				// Draw lines for every other two control points if theres no lock
				canvas.beginPath();
		 		if (this.control == 1) {canvas.strokeStyle = "#FF0000";} else {canvas.strokeStyle = "#fff";}
		 		for (i = 0; i < this.points.length; i++){this.points[i].strokeColor = canvas.strokeStyle;}

		 		for (i = 2; i < this.points.length; i+=2)
				{
					if (this.control == 1) {
						canvas.moveTo(this.points[i-1].x, this.points[i-1].y);
						canvas.lineTo(this.points[i].x, this.points[i].y);
					}

				}
				canvas.stroke();
	 			canvas.closePath();

				c = bezierCurve2(this.points, $("#points").val() );

				canvas.beginPath();
				canvas.strokeStyle = "#FF0000";
				for (t = 0; t < c.length ; t++) {
					canvas.fillRect(c[t].x,c[t].y,1,1);
				}
				canvas.stroke();
		 		canvas.closePath();

			});

			// Pressing x while the canvas is selected adds a small draggable circle to the canvas
			// As well as a line connecting it to the previous circle or the mid point of the canvas if its the first click
			canvas.bind("dblclick", function (x) {

			        // Right button press
				if (x.which == 1) {
					// Detect if button press is within an existing circle
					var detectbit = 0;
					if (pointCounter > 0) {
						for (i = 0; i < pointArray.length; i++)
						{
							if ( Math.pow( canvas.mouse.x - pointArray[i].x, 2) + Math.pow( canvas.mouse.y - pointArray[i].y, 2) < Math.pow(pointArray[i].radius,2)) {
								detectbit = 1;
							}
						}
					}

					// If not in an existing circle then add a new circle
					if (detectbit == 0) {
						pointCounter++;
						// if this is the first small circle add an extra small circle at the midpoint of the start of the canvas
						if (pointCounter == 1) {
							var point = canvas.display.ellipse({x: 0, y: canvas.height / 2, radius: 5,stroke: "1px #FF0000"});
							canvas.addChild(point);
							pointArray[pointArray.length] = point;
							bezier = canvas.display.bezierCurve({points: pointArray, stroke: "1px #000", control: 1});
							canvas.addChild(bezier);
						}
		    				var point = canvas.display.ellipse({x: canvas.mouse.x, y: canvas.mouse.y, radius: 5,stroke: "1px #FF0000"});
		    				pointArray[pointArray.length] = point;
						canvas.addChild(point);
						var dragOptions = { changeZindex: false };
						point.dragAndDrop(dragOptions);
					}

				}

			});

			canvas.bind("keydown", function (x) {

				// Spacebar turns on and off the control points and lines of the bezier object
				if (x.which == 32) {
					if(bezier.control == 0) {
						bezier.control = 1;
						canvas.redraw();
					} else {
						bezier.control = 0;
						canvas.redraw();
					}
				}

				// the letter E creates an endpoint
				if (x.which == 69) {
					var point = canvas.display.ellipse({x: canvas.width, y: canvas.height / 2, radius: 5,stroke: "1px #FF0000"});
		    			pointArray[pointArray.length] = point;
					canvas.addChild(point);
				}

				// the letter C clears the canvas and resets the global variables
				if (x.which == 67) {
					canvas.children = [];
					canvas.clear();
					pointCounter = 0;
					pointArray = [];
					bezier = null;
				}

			});

			$( '#play' ).click ( function () {

			        var wave = [];

			        wave = bezierCurve2(pointArray, $("#points").val());

			        // Stereo
				var channels = 2;

				// Create an empty duration seconds stereo buffer at the
				// sample rate of the AudioContext
				var frameCount = audioCtx.sampleRate * $("#duration").val();

				var myArrayBuffer = audioCtx.createBuffer(channels, frameCount, audioCtx.sampleRate);

				// Fill the buffer with my invented sound and the complement
				for (var channel = 0; channel < channels; channel++) {
					// This gives us the actual array that contains the data
					var nowBuffering = myArrayBuffer.getChannelData(channel);

				// loop over the entire frame count
				for (var j = 0; j < Math.floor(frameCount / wave.length); j++)

					for (var i = 0; i < wave.length; i++) {
						nowBuffering[(j* wave.length) + i] = ((wave[i].y - (canvas.height / 2)) / canvas.height);
						nowBuffering[(j* wave.length) + wave.length + i] = (-1 * (wave[i].y - (canvas.height / 2)) / canvas.height);
					}

				}

				// Get an AudioBufferSourceNode
				// This is the AudioNode to use when we want to play an AudioBuffer
				var source = audioCtx.createBufferSource();

				// set the buffer in the AudioBufferSourceNode
				source.buffer = myArrayBuffer;

				// connect the AudioBufferSourceNode to the
				// destination so we can hear the sound
				source.connect(audioCtx.destination);

				// start the source playing
				source.start();

			});

	});
	<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 PrimeBox (info@primebox.co.uk)
 *
 * This work is licensed under the Creative Commons
 * Attribution 3.0 Unported License. To view a copy
 * of this license, visit
 * http://creativecommons.org/licenses/by/3.0/.
 *
 * Documentation available at:
 * http://www.primebox.co.uk/projects/cookie-bar/
 *
 * When using this software you use it at your own risk. We hold
 * no responsibility for any damage caused by using this plugin
 * or the documentation provided.
 */
 !function(a){a.cookieBar=function(b,c){if("cookies"==b)var d="cookies";else if("set"==b)var d="set";else var d=!1;var e={message:"We use cookies to track usage and preferences.",acceptButton:!0,acceptText:"I Understand",declineButton:!1,declineText:"Disable Cookies",policyButton:!1,policyText:"Privacy Policy",policyURL:"/privacy-policy/",autoEnable:!0,acceptOnContinue:!1,expireDays:365,forceShow:!1,effect:"slide",element:"body",append:!1,fixed:!1,bottom:!1,zindex:"",redirect:String(window.location.href),domain:String(window.location.hostname),referrer:String(document.referrer),acceptFunction:!1,declineFunction:!1,customClass:""},b=a.extend(e,b),f=new Date;f.setTime(f.getTime()+24*b.expireDays*60*60*1e3),f=f.toGMTString();var g,h,i="cb-enabled={value}; expires="+f+"; path=/",j="",k=document.cookie.split("; ");for(g=0;g<k.length;g++)h=k[g].split("="),"cb-enabled"==h[0]&&(j=h[1]);if(""==j&&"cookies"!=d&&b.autoEnable&&(j="enabled",document.cookie=i.replace("{value}","enabled")),b.acceptOnContinue&&b.referrer.indexOf(b.domain)>=0&&-1==String(window.location.href).indexOf(b.policyURL)&&"cookies"!=d&&"set"!=d&&"accepted"!=j&&"declined"!=j&&(d="set",c="accepted"),"cookies"==d)return"enabled"==j||"accepted"==j?!0:!1;if("set"==d&&("accepted"==c||"declined"==c))return document.cookie=i.replace("{value}",c),"accepted"==c?!0:!1;var l=b.message.replace("{policy_url}",b.policyURL);if(b.acceptButton)var m=''+b.acceptText+"";else var m="";if(b.declineButton)var n=''+b.declineText+"";else var n="";if(b.policyButton)var o=''+b.policyText+"";else var o="";if(b.fixed)if(b.bottom)var p=' class="fixed bottom '+b.customClass+'"';else var p=' class="fixed '+b.customClass+'"';else var p=' class="'+b.customClass+'"';if(""!=b.zindex)var q=' style="z-index:'+b.zindex+';"';else var q="";(b.forceShow||"enabled"==j||""==j)&&(b.append?a(b.element).append('

"+l+m+n+o+"

"):a(b.element).prepend('

"+l+m+n+o+"

")),a("#cookie-bar .cb-enable").click(function(){return document.cookie=i.replace("{value}","accepted"),b.acceptFunction&&"function"==typeof b.acceptFunction&&b.acceptFunction(),"enabled"==j||"accepted"==j?("slide"==b.effect?a("#cookie-bar").slideUp(300,function(){a("#cookie-bar").remove()}):"fade"==b.effect?a("#cookie-bar").fadeOut(300,function(){a("#cookie-bar").remove()}):a("#cookie-bar").hide(0,function(){a("#cookie-bar").remove()}),!1):void(b.redirect!==!1&&(window.location=b.redirect))}),a("#cookie-bar .cb-disable").click(function(){var c=new Date;for(c.setTime(c.getTime()-864e6),c=c.toGMTString(),k=document.cookie.split("; "),g=0;g<k.length;g++)h=k[g].split("="),h[0].indexOf("_")>=0?document.cookie=h[0]+"=0; expires="+c+"; domain="+b.domain.replace("www","")+"; path=/":document.cookie=h[0]+"=0; expires="+c+"; path=/";return document.cookie=i.replace("{value}","declined"),b.declineFunction&&"function"==typeof b.declineFunction&&b.declineFunction(),"enabled"!=j||"accepted"==j?("slide"==b.effect?a("#cookie-bar").slideUp(300,function(){a("#cookie-bar").remove()}):"fade"==b.effect?a("#cookie-bar").fadeOut(300,function(){a("#cookie-bar").remove()}):a("#cookie-bar").hide(0,function(){a("#cookie-bar").remove()}),!1):void(b.redirect!==!1&&(window.location=b.redirect))})}}(jQuery);<commit_msg>Updated jquery.cookiebar.min.js.<commit_after>/*
 * Copyright (C) 2012 PrimeBox (info@primebox.co.uk)
 *
 * This work is licensed under the Creative Commons
 * Attribution 3.0 Unported License. To view a copy
 * of this license, visit
 * http://creativecommons.org/licenses/by/3.0/.
 *
 * Documentation available at:
 * http://www.primebox.co.uk/projects/cookie-bar/
 *
 * When using this software you use it at your own risk. We hold
 * no responsibility for any damage caused by using this plugin
 * or the documentation provided.
 */
 !function(a){a.cookieBar=function(b,c){if("cookies"==b)var d="cookies";else if("set"==b)var d="set";else var d=!1;var e={message:"We use cookies to track usage and preferences.",acceptButton:!0,acceptText:"I Understand",acceptFunction:!1,declineButton:!1,declineText:"Disable Cookies",declineFunction:!1,policyButton:!1,policyText:"Privacy Policy",policyFunction:!1,policyURL:"/privacy-policy/",autoEnable:!0,acceptOnContinue:!1,expireDays:365,forceShow:!1,effect:"slide",element:"body",append:!1,fixed:!1,bottom:!1,customClass:"",zindex:"",redirect:String(window.location.href),domain:String(window.location.hostname),referrer:String(document.referrer)},b=a.extend(e,b),f=new Date;f.setTime(f.getTime()+24*b.expireDays*60*60*1e3),f=f.toGMTString();var g,h,i="cb-enabled={value}; expires="+f+"; path=/",j="",k=document.cookie.split("; ");for(g=0;g<k.length;g++)h=k[g].split("="),"cb-enabled"==h[0]&&(j=h[1]);if(""==j&&"cookies"!=d&&b.autoEnable&&(j="enabled",document.cookie=i.replace("{value}","enabled")),b.acceptOnContinue&&b.referrer.indexOf(b.domain)>=0&&-1==String(window.location.href).indexOf(b.policyURL)&&"cookies"!=d&&"set"!=d&&"accepted"!=j&&"declined"!=j&&(d="set",c="accepted"),"cookies"==d)return"enabled"==j||"accepted"==j?!0:!1;if("set"==d&&("accepted"==c||"declined"==c))return document.cookie=i.replace("{value}",c),"accepted"==c?!0:!1;var l=b.message.replace("{policy_url}",b.policyURL);if(b.acceptButton)var m=''+b.acceptText+"";else var m="";if(b.declineButton)var n=''+b.declineText+"";else var n="";if(b.policyButton)var o=''+b.policyText+"";else var o="";if(b.fixed)if(b.bottom)var p=' class="fixed bottom '+b.customClass+'"';else var p=' class="fixed '+b.customClass+'"';else var p=' class="'+b.customClass+'"';if(""!=b.zindex)var q=' style="z-index:'+b.zindex+';"';else var q="";(b.forceShow||"enabled"==j||""==j)&&(b.append?a(b.element).append('

"+l+m+n+o+"

"):a(b.element).prepend('

"+l+m+n+o+"

")),a("#cookie-bar .cb-enable").click(function(){return document.cookie=i.replace("{value}","accepted"),b.acceptFunction&&"function"==typeof b.acceptFunction&&b.acceptFunction(),"enabled"==j||"accepted"==j?("slide"==b.effect?a("#cookie-bar").slideUp(300,function(){a("#cookie-bar").remove()}):"fade"==b.effect?a("#cookie-bar").fadeOut(300,function(){a("#cookie-bar").remove()}):a("#cookie-bar").hide(0,function(){a("#cookie-bar").remove()}),!1):void(b.redirect!==!1&&(window.location=b.redirect))}),a("#cookie-bar .cb-disable").click(function(){var c=new Date;for(c.setTime(c.getTime()-864e6),c=c.toGMTString(),k=document.cookie.split("; "),g=0;g<k.length;g++)h=k[g].split("="),h[0].indexOf("_")>=0?document.cookie=h[0]+"=0; expires="+c+"; domain="+b.domain.replace("www","")+"; path=/":document.cookie=h[0]+"=0; expires="+c+"; path=/";return document.cookie=i.replace("{value}","declined"),b.declineFunction&&"function"==typeof b.declineFunction&&b.declineFunction(),"enabled"!=j||"accepted"==j?("slide"==b.effect?a("#cookie-bar").slideUp(300,function(){a("#cookie-bar").remove()}):"fade"==b.effect?a("#cookie-bar").fadeOut(300,function(){a("#cookie-bar").remove()}):a("#cookie-bar").hide(0,function(){a("#cookie-bar").remove()}),!1):void(b.redirect!==!1&&(window.location=b.redirect))}),a("#cookie-bar .cb-policy").click(function(){b.policyFunction&&"function"==typeof b.policyFunction&&b.policyFunction()})}}(jQuery);<|endoftext|>


<commit_before>const { expect } = require("chai")

describe("Chevrotain Webpacking support", () => {
    it("Can be webpacked without any special configuration when the grammar and tokens are defined in the same file", () => {
        const parse = require("../lib/webpacked.min").parse
        const parseResult = parse("[1,2,3]")
        expect(parseResult.errors).to.be.empty
    })
})
<commit_msg>Fix failing build under node.js 6<commit_after>const { expect } = require("chai")
const { lt } = require("semver")

// not sure why this fails on nodejs 6 nor do I believe it is relevant to the example.
let itButSkipOnNode6 = lt(process.version, "8.0.0") ? it.skip : it

describe("Chevrotain Webpacking support", () => {
    itButSkipOnNode6(
        "Can be webpacked by configuring reserved words for name mangling",
        () => {
            const parse = require("../lib/webpacked.min").parse
            const parseResult = parse("[1,2,3]")
            expect(parseResult.errors).to.be.empty
        }
    )
})
<|endoftext|>


<commit_before>(function($) {

	function SuperheroCard(name, photoUrl) {
		var self = this;
		self.name = name;
		self.photoUrl = photoUrl;
	}

	function init() {
		ko.applyBindings(new SuperheroCard("Batman", "../../../images/avatars/batman.jpg"));
	}

	$(document).ready(function() {
		init();
	})
})(jQuery);<commit_msg>SuperheroCard -> SuperheroCardModel<commit_after>(function($) {

	function SuperheroCardModel(name, photoUrl) {
		var self = this;
		self.name = name;
		self.photoUrl = photoUrl;
	}

	function init() {
		ko.applyBindings(new SuperheroCardModel("Batman", "../../../images/avatars/batman.jpg"));
	}

	$(document).ready(function() {
		init();
	})
})(jQuery);<|endoftext|>


<commit_before>import React from 'react';
import Select from 'react-select';

var RemoteSelectField = React.createClass({
	displayName: 'RemoteSelectField',
	propTypes: {
		label: React.PropTypes.string,
	},
	loadOptions (input, callback) {
		input = input.toLowerCase();
		var rtn = {
			options: [
				{ label: 'One', value: 'one' },
				{ label: 'Two', value: 'two' },
				{ label: 'Three', value: 'three' }
			],
			complete: true
		};
		if (input.slice(0, 1) === 'a') {
			if (input.slice(0, 2) === 'ab') {
				rtn = {
					options: [
						{ label: 'AB', value: 'ab' },
						{ label: 'ABC', value: 'abc' },
						{ label: 'ABCD', value: 'abcd' }
					],
					complete: true
				};
			} else {
				rtn = {
					options: [
						{ label: 'A', value: 'a' },
						{ label: 'AA', value: 'aa' },
						{ label: 'AB', value: 'ab' }
					],
					complete: false
				};
			}
		} else if (!input.length) {
			rtn.complete = false;
		}

		setTimeout(function() {
			callback(null, rtn);
		}, 500);
	},
	renderHint () {
		if (!this.props.hint) return null;
		return (

{this.props.hint}

		);
	},
	render () {
		return (

{this.props.label}

				<Select asyncOptions={this.loadOptions} className="remote-example" />
				{this.renderHint()}

		);
	}
});

module.exports = RemoteSelectField;<commit_msg>Adding hint propType; fixes linter warning<commit_after>import React from 'react';
import Select from 'react-select';

var RemoteSelectField = React.createClass({
	displayName: 'RemoteSelectField',
	propTypes: {
		hint: React.PropTypes.string,
		label: React.PropTypes.string,
	},
	loadOptions (input, callback) {
		input = input.toLowerCase();
		var rtn = {
			options: [
				{ label: 'One', value: 'one' },
				{ label: 'Two', value: 'two' },
				{ label: 'Three', value: 'three' }
			],
			complete: true
		};
		if (input.slice(0, 1) === 'a') {
			if (input.slice(0, 2) === 'ab') {
				rtn = {
					options: [
						{ label: 'AB', value: 'ab' },
						{ label: 'ABC', value: 'abc' },
						{ label: 'ABCD', value: 'abcd' }
					],
					complete: true
				};
			} else {
				rtn = {
					options: [
						{ label: 'A', value: 'a' },
						{ label: 'AA', value: 'aa' },
						{ label: 'AB', value: 'ab' }
					],
					complete: false
				};
			}
		} else if (!input.length) {
			rtn.complete = false;
		}

		setTimeout(function() {
			callback(null, rtn);
		}, 500);
	},
	renderHint () {
		if (!this.props.hint) return null;
		return (

{this.props.hint}

		);
	},
	render () {
		return (

{this.props.label}

				<Select asyncOptions={this.loadOptions} className="remote-example" />
				{this.renderHint()}

		);
	}
});

module.exports = RemoteSelectField;
<|endoftext|>