<commit_before>
function websocket (env, server, entries) {
"use strict";
// CONSTANTS
var ONE_HOUR = 3600000,
    ONE_MINUTE = 60000,
    FIVE_MINUTES = 300000,
    FORTY_MINUTES = 2400000,
    TWO_DAYS = 172800000;

var dir2Char = {
  'NONE': '⇼',
  'DoubleUp': '⇈',
  'SingleUp': '↑',
  'FortyFiveUp': '↗',
  'Flat': '→',
  'FortyFiveDown': '↘',
  'SingleDown': '↓',
  'DoubleDown': '⇊',
  'NOT COMPUTABLE': '-',
  'RATE OUT OF RANGE': '↮'
};

  var io;
  var watchers = 0;
  var now = new Date().getTime();
  var cgmData = [];
  var patientData = [];

  function start ( ) {
    io = require('socket.io').listen(server);
  }
  // get data from database and setup to update every minute
  function kickstart (fn) {
    //TODO: test server to see how data is stored (timestamps, entry values, etc)
    //TODO: check server settings to configure alerts, entry units, etc
    console.log(now, new Date(now), fn.name);
    fn( );
    return fn;
  }

  function emitData ( ) {
    console.log('running emitData', now, patientData && patientData.length);
    if (patientData.length > 0) {
      io.sockets.emit("now", now);
      io.sockets.emit("sgv", patientData);
    }
  }

  function configure ( ) {
    // reduce logging
    io.set('log level', 0);

    //Windows Azure Web Sites does not currently support WebSockets, so use long-polling
    io.configure(function () {
        io.set('transports', ['xhr-polling']);
    });
  }

  function listeners ( ) {
    io.sockets.on('connection', function (socket) {
        io.sockets.emit("now", now);
        io.sockets.emit("sgv", patientData);
        io.sockets.emit("clients", ++watchers);
        socket.on('ack', function(alarmType, _silenceTime) {
            alarms[alarmType].lastAckTime = new Date().getTime();
            alarms[alarmType].silenceTime = _silenceTime ? _silenceTime : FORTY_MINUTES;
            io.sockets.emit("clear_alarm", true);
            console.log("alarm cleared");
        });
        socket.on('disconnect', function () {
            io.sockets.emit("clients", --watchers);
        });
    });
  }

///////////////////////////////////////////////////
// data handling functions
///////////////////////////////////////////////////

function directionToChar(direction) {
  return dir2Char[direction] || '-';
}

var Alarm = function(_typeName, _threshold) {
    this.typeName = _typeName;
    this.silenceTime = FORTY_MINUTES;
    this.lastAckTime = 0;
    this.threshold = _threshold;
};

// list of alarms with their thresholds
var alarms = {
    "alarm" : new Alarm("Regular", 0.05),
    "urgent_alarm": new Alarm("Urgent", 0.10)
};

function update() {

    console.log('running update');
    now = Date.now();

    cgmData = [];
    var earliest_data = now - TWO_DAYS;
    var q = { find: {"date": {"$gte": earliest_data}} };
    entries.list(q, function (err, results) {
      results.forEach(function(element, index, array) {
          if (element) {
              var obj = {};
              obj.y = element.sgv;
              obj.x = element.date;
              obj.d = element.dateString;
              obj.direction = directionToChar(element.direction);
              cgmData.push(obj);
          }
      });
      // all done, do loadData
      loadData( );
    });

    return update;
}

function emitAlarm(alarmType) {
    var alarm = alarms[alarmType];
    if (now > alarm.lastAckTime + alarm.silenceTime) {
        io.sockets.emit(alarmType);
    } else {
        console.log(alarm.typeName + " alarm is silenced for " + Math.floor((alarm.silenceTime - (now - alarm.lastAckTime)) / 60000) + " minutes more");
    }
}

function loadData() {

    console.log('running loadData');
    var treatment = [];
    var mbg = [];

    var actual = [];
    if (cgmData) {
        actual = cgmData.slice();
        actual.sort(function(a, b) {
            return a.x - b.x;
        });

        // sgv less than or equal to 10 means error code
        // or warm up period code, so ignore
        actual = actual.filter(function (a) {
            return a.y > 10;
        })
    }

    var actualLength = actual.length - 1;

    if (actualLength > 1) {
        // predict using AR model
        var predicted = [];
        var predictedCI = [];
        var lastValidReadingTime = actual[actualLength].x;
        var elapsedMins = (actual[actualLength].x - actual[actualLength - 1].x) / ONE_MINUTE;
        var BG_REF = 140;
        var BG_MIN = 36;
        var BG_MAX = 400;
        var y = Math.log(actual[actualLength].y / BG_REF);
        if (elapsedMins < 5.1) {
            y = [Math.log(actual[actualLength - 1].y / BG_REF), y];
        } else {
            y = [y, y];
        }
        var n = Math.ceil(12 * (1 / 2 + (now - lastValidReadingTime) / ONE_HOUR));
        var AR = [-0.723, 1.716];
        var dt = actual[actualLength].x;
        for (var i = 0; i <= n; i++) {
            y = [y[1], AR[0] * y[0] + AR[1] * y[1]];
            dt = dt + FIVE_MINUTES;
            predicted[i] = {
                x: dt,
                y: Math.max(BG_MIN, Math.min(BG_MAX, Math.round(BG_REF * Math.exp(y[1]))))
            };
        }

        //TODO: need to consider when data being sent has less than the 2 day minimum

        // consolidate and send the data to the client
        var shouldEmit = is_different(actual, predicted, mbg, treatment);
        patientData = [actual, predicted, mbg, treatment];
        console.log('patientData', patientData.length);
        if (shouldEmit) {
          emitData( );
        }

        // compute current loss
        var avgLoss = 0;
        var size = Math.min(predicted.length - 1, 6);
        for (var j = 0; j <= size; j++) {
            avgLoss += 1 / size * Math.pow(log10(predicted[j].y / 120), 2);
        }

        if (avgLoss > alarms['urgent_alarm'].threshold) {
            emitAlarm('urgent_alarm');
        } else if (avgLoss > alarms['alarm'].threshold) {
            emitAlarm('alarm');
        }
    }
}
  function is_different (actual, predicted, mbg, treatment) {
    if (patientData && patientData.length < 3) {
      return true;
    }
    var old =  {
        actual: patientData[0].slice(-1).pop( )
      , predicted: patientData[1].slice(-1).pop( )
      , mbg: patientData[2].slice(-1).pop( )
      , treatment: patientData[3].slice(-1).pop( )
    };
    var last = {
        actual: actual.slice(-1).pop( )
      , predicted: predicted.slice(-1).pop( )
      , mbg: mbg.slice(-1).pop( )
      , treatment: treatment.slice(-1).pop( )
    };

    // textual diff of objects
    if (JSON.stringify(old) == JSON.stringify(last)) {
      return false;
    }
    return true;
  }

  start( );
  configure( );
  listeners( );
  setInterval(kickstart(update), ONE_MINUTE);
  setInterval(kickstart(emitData), ONE_MINUTE);

  return io;
}

///////////////////////////////////////////////////
// helper functions
///////////////////////////////////////////////////

function log10(val) { return Math.log(val) / Math.LN10; }

module.exports = websocket;
<commit_msg>Removed dead code.<commit_after>
function websocket (env, server, entries) {
"use strict";
// CONSTANTS
var ONE_HOUR = 3600000,
    ONE_MINUTE = 60000,
    FIVE_MINUTES = 300000,
    FORTY_MINUTES = 2400000,
    TWO_DAYS = 172800000;

var dir2Char = {
  'NONE': '⇼',
  'DoubleUp': '⇈',
  'SingleUp': '↑',
  'FortyFiveUp': '↗',
  'Flat': '→',
  'FortyFiveDown': '↘',
  'SingleDown': '↓',
  'DoubleDown': '⇊',
  'NOT COMPUTABLE': '-',
  'RATE OUT OF RANGE': '↮'
};

  var io;
  var watchers = 0;
  var now = new Date().getTime();
  var cgmData = [];
  var patientData = [];

  function start ( ) {
    io = require('socket.io').listen(server);
  }
  // get data from database and setup to update every minute
  function kickstart (fn) {
    //TODO: test server to see how data is stored (timestamps, entry values, etc)
    //TODO: check server settings to configure alerts, entry units, etc
    console.log(now, new Date(now), fn.name);
    fn( );
    return fn;
  }

  function emitData ( ) {
    console.log('running emitData', now, patientData && patientData.length);
    if (patientData.length > 0) {
      io.sockets.emit("now", now);
      io.sockets.emit("sgv", patientData);
    }
  }

  function configure ( ) {
    // reduce logging
    io.set('log level', 0);

    //Windows Azure Web Sites does not currently support WebSockets, so use long-polling
    io.configure(function () {
        io.set('transports', ['xhr-polling']);
    });
  }

  function listeners ( ) {
    io.sockets.on('connection', function (socket) {
        io.sockets.emit("now", now);
        io.sockets.emit("sgv", patientData);
        io.sockets.emit("clients", ++watchers);
        socket.on('ack', function(alarmType, _silenceTime) {
            alarms[alarmType].lastAckTime = new Date().getTime();
            alarms[alarmType].silenceTime = _silenceTime ? _silenceTime : FORTY_MINUTES;
            io.sockets.emit("clear_alarm", true);
            console.log("alarm cleared");
        });
        socket.on('disconnect', function () {
            io.sockets.emit("clients", --watchers);
        });
    });
  }

///////////////////////////////////////////////////
// data handling functions
///////////////////////////////////////////////////

function directionToChar(direction) {
  return dir2Char[direction] || '-';
}

var Alarm = function(_typeName, _threshold) {
    this.typeName = _typeName;
    this.silenceTime = FORTY_MINUTES;
    this.lastAckTime = 0;
    this.threshold = _threshold;
};

// list of alarms with their thresholds
var alarms = {
    "alarm" : new Alarm("Regular", 0.05),
    "urgent_alarm": new Alarm("Urgent", 0.10)
};

function update() {

    console.log('running update');
    now = Date.now();

    cgmData = [];
    var earliest_data = now - TWO_DAYS;
    var q = { find: {"date": {"$gte": earliest_data}} };
    entries.list(q, function (err, results) {
      results.forEach(function(element, index, array) {
          if (element) {
              var obj = {};
              obj.y = element.sgv;
              obj.x = element.date;
              obj.d = element.dateString;
              obj.direction = directionToChar(element.direction);
              cgmData.push(obj);
          }
      });
      // all done, do loadData
      loadData( );
    });

    return update;
}

function emitAlarm(alarmType) {
    var alarm = alarms[alarmType];
    if (now > alarm.lastAckTime + alarm.silenceTime) {
        io.sockets.emit(alarmType);
    } else {
        console.log(alarm.typeName + " alarm is silenced for " + Math.floor((alarm.silenceTime - (now - alarm.lastAckTime)) / 60000) + " minutes more");
    }
}

function loadData() {

    console.log('running loadData');
    var treatment = [];
    var mbg = [];

    var actual = [];
    if (cgmData) {
        actual = cgmData.slice();
        actual.sort(function(a, b) {
            return a.x - b.x;
        });

        // sgv less than or equal to 10 means error code
        // or warm up period code, so ignore
        actual = actual.filter(function (a) {
            return a.y > 10;
        })
    }

    var actualLength = actual.length - 1;

    if (actualLength > 1) {
        // predict using AR model
        var predicted = [];
        var lastValidReadingTime = actual[actualLength].x;
        var elapsedMins = (actual[actualLength].x - actual[actualLength - 1].x) / ONE_MINUTE;
        var BG_REF = 140;
        var BG_MIN = 36;
        var BG_MAX = 400;
        var y = Math.log(actual[actualLength].y / BG_REF);
        if (elapsedMins < 5.1) {
            y = [Math.log(actual[actualLength - 1].y / BG_REF), y];
        } else {
            y = [y, y];
        }
        var n = Math.ceil(12 * (1 / 2 + (now - lastValidReadingTime) / ONE_HOUR));
        var AR = [-0.723, 1.716];
        var dt = actual[actualLength].x;
        for (var i = 0; i <= n; i++) {
            y = [y[1], AR[0] * y[0] + AR[1] * y[1]];
            dt = dt + FIVE_MINUTES;
            predicted[i] = {
                x: dt,
                y: Math.max(BG_MIN, Math.min(BG_MAX, Math.round(BG_REF * Math.exp(y[1]))))
            };
        }

        //TODO: need to consider when data being sent has less than the 2 day minimum

        // consolidate and send the data to the client
        var shouldEmit = is_different(actual, predicted, mbg, treatment);
        patientData = [actual, predicted, mbg, treatment];
        console.log('patientData', patientData.length);
        if (shouldEmit) {
          emitData( );
        }

        // compute current loss
        var avgLoss = 0;
        var size = Math.min(predicted.length - 1, 6);
        for (var j = 0; j <= size; j++) {
            avgLoss += 1 / size * Math.pow(log10(predicted[j].y / 120), 2);
        }

        if (avgLoss > alarms['urgent_alarm'].threshold) {
            emitAlarm('urgent_alarm');
        } else if (avgLoss > alarms['alarm'].threshold) {
            emitAlarm('alarm');
        }
    }
}
  function is_different (actual, predicted, mbg, treatment) {
    if (patientData && patientData.length < 3) {
      return true;
    }
    var old =  {
        actual: patientData[0].slice(-1).pop( )
      , predicted: patientData[1].slice(-1).pop( )
      , mbg: patientData[2].slice(-1).pop( )
      , treatment: patientData[3].slice(-1).pop( )
    };
    var last = {
        actual: actual.slice(-1).pop( )
      , predicted: predicted.slice(-1).pop( )
      , mbg: mbg.slice(-1).pop( )
      , treatment: treatment.slice(-1).pop( )
    };

    // textual diff of objects
    if (JSON.stringify(old) == JSON.stringify(last)) {
      return false;
    }
    return true;
  }

  start( );
  configure( );
  listeners( );
  setInterval(kickstart(update), ONE_MINUTE);
  setInterval(kickstart(emitData), ONE_MINUTE);

  return io;
}

///////////////////////////////////////////////////
// helper functions
///////////////////////////////////////////////////

function log10(val) { return Math.log(val) / Math.LN10; }

module.exports = websocket;
<|endoftext|>


<commit_before>var crypto = require('crypto');

function selectMechanism(mechs) {
    if (mechs.indexOf("ANONYMOUS") >= 0)
	return new Anonymous();
    else if (mechs.indexOf("DIGEST-MD5") >= 0)
	return new DigestMD5();
    else if (mechs.indexOf("PLAIN") >= 0)
	return new Plain();
    else
	return null;
}
exports.selectMechanism = selectMechanism;

function Plain() {
    this.name = "PLAIN";
    this.auth = function() {
	return this.authzid + "\0" +
	    this.authcid + "\0" +
	    this.password;
    };
}

function Anonymous() {
    this.name = "ANONYMOUS";
    this.auth = function() {
	return this.authzid;
    };
}

function DigestMD5() {
    this.name = "DIGEST-MD5";
    this.auth = function() {
	return "";
    };

    this.nonce_count = 0;
    this.getNC = function() {
	return rjust(this.nonce_count.toString(), 8, '0');
    };
    this.cnonce = generateNonce();
    this.challenge = function(s) {
	dict = parseDict(s);
	if (dict.realm)
	    this.realm = dict.realm;
	//require('sys').puts("dict: "+JSON.stringify(dict));

	var response;
	if (dict.nonce && dict.qop) {
	    this.nonce_count++;
	    var a1 = md5(this.authcid + ':' +
			 this.realm + ':' +
			 this.password) + ':' +
			     dict.nonce + ':' +
			     this.cnonce + ':' +
			     this.authzid;
	    var a2 = "AUTHENTICATE:" + this.digest_uri;
	    if (dict.qop == 'auth-int' || dict.qop == 'auth-conf')
		a2 += ":00000000000000000000000000000000";
	    var responseValue = md5_hex(md5_hex(a1) + ':' +
					dict.nonce + ':' +
					this.getNC() + ':' +
					this.cnonce + ':' +
					dict.qop + ':' +
					md5_hex(a2)
				       );
	    response = {
		username: this.authcid,
		realm: this.realm,
		nonce: dict.nonce,
		cnonce: this.cnonce,
		nc: this.getNC(),
		qop: dict.qop,
		'digest-uri': this.digest_uri,
		response: responseValue,
		authzid: this.authzid,
		charset: 'utf-8'
	    };
	} else if (dict.rspauth) {
	    return "";
	}
	//require('sys').puts('response: '+JSON.stringify(response));
	return encodeDict(response);
    };
}

function parseDict(s) {
    var result = {};
    while (s) {
	var m;

	if ((m = /^(.+?)="(.*?[^\\])",(.*)/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else if ((m = /^(.+?)="(.*?[^\\])"$/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else if ((m = /^(.+?)=(.+?),(.*)/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else if ((m = /^(.+?)=(.+?)$/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else {
	    s = null;
	}
    }
    return result;
}

function encodeDict(dict) {
    var s = "";
    for(k in dict) {
	var v = dict[k];
	if (v)
	    s += ',' + k + '="' + v + '"';
    }
    return s.substr(1);  // without first ','
}

function rjust(s, targetLen, padding) {
    while(s.length < targetLen)
	s = padding + s;
    return s;
}

function md5(s, encoding) {
    var hash = crypto.createHash('md5');
    hash.update(s);
    return hash.digest(encoding || 'binary');
}

function md5_hex(s) {
    return md5(s, 'hex');
}

function generateNonce() {
    var result = "";
    for(var i = 0; i < 8; i++)
	result += String.fromCharCode(48 +
				      Math.round(Math.random() * 10));
    return result;
}
<commit_msg>sasl generateNonce(): fix<commit_after>var crypto = require('crypto');

function selectMechanism(mechs) {
    if (mechs.indexOf("ANONYMOUS") >= 0)
	return new Anonymous();
    else if (mechs.indexOf("DIGEST-MD5") >= 0)
	return new DigestMD5();
    else if (mechs.indexOf("PLAIN") >= 0)
	return new Plain();
    else
	return null;
}
exports.selectMechanism = selectMechanism;

function Plain() {
    this.name = "PLAIN";
    this.auth = function() {
	return this.authzid + "\0" +
	    this.authcid + "\0" +
	    this.password;
    };
}

function Anonymous() {
    this.name = "ANONYMOUS";
    this.auth = function() {
	return this.authzid;
    };
}

function DigestMD5() {
    this.name = "DIGEST-MD5";
    this.auth = function() {
	return "";
    };

    this.nonce_count = 0;
    this.getNC = function() {
	return rjust(this.nonce_count.toString(), 8, '0');
    };
    this.cnonce = generateNonce();
    this.challenge = function(s) {
	dict = parseDict(s);
	if (dict.realm)
	    this.realm = dict.realm;
	//require('sys').puts("dict: "+JSON.stringify(dict));

	var response;
	if (dict.nonce && dict.qop) {
	    this.nonce_count++;
	    var a1 = md5(this.authcid + ':' +
			 this.realm + ':' +
			 this.password) + ':' +
			     dict.nonce + ':' +
			     this.cnonce + ':' +
			     this.authzid;
	    var a2 = "AUTHENTICATE:" + this.digest_uri;
	    if (dict.qop == 'auth-int' || dict.qop == 'auth-conf')
		a2 += ":00000000000000000000000000000000";
	    var responseValue = md5_hex(md5_hex(a1) + ':' +
					dict.nonce + ':' +
					this.getNC() + ':' +
					this.cnonce + ':' +
					dict.qop + ':' +
					md5_hex(a2)
				       );
	    response = {
		username: this.authcid,
		realm: this.realm,
		nonce: dict.nonce,
		cnonce: this.cnonce,
		nc: this.getNC(),
		qop: dict.qop,
		'digest-uri': this.digest_uri,
		response: responseValue,
		authzid: this.authzid,
		charset: 'utf-8'
	    };
	} else if (dict.rspauth) {
	    return "";
	}
	//require('sys').puts('response: '+JSON.stringify(response));
	return encodeDict(response);
    };
}

function parseDict(s) {
    var result = {};
    while (s) {
	var m;

	if ((m = /^(.+?)="(.*?[^\\])",(.*)/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else if ((m = /^(.+?)="(.*?[^\\])"$/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else if ((m = /^(.+?)=(.+?),(.*)/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else if ((m = /^(.+?)=(.+?)$/.exec(s))) {
	    result[m[1]] = m[2];
	    s = m[3];
	} else {
	    s = null;
	}
    }
    return result;
}

function encodeDict(dict) {
    var s = "";
    for(k in dict) {
	var v = dict[k];
	if (v)
	    s += ',' + k + '="' + v + '"';
    }
    return s.substr(1);  // without first ','
}

function rjust(s, targetLen, padding) {
    while(s.length < targetLen)
	s = padding + s;
    return s;
}

function md5(s, encoding) {
    var hash = crypto.createHash('md5');
    hash.update(s);
    return hash.digest(encoding || 'binary');
}

function md5_hex(s) {
    return md5(s, 'hex');
}

function generateNonce() {
    var result = "";
    for(var i = 0; i < 8; i++)
	result += String.fromCharCode(48 +
				      Math.ceil(Math.random() * 10));
    return result;
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Sebastian
// Date:      02.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/view_manager.js');

/**
 * @class
 *
 * The root class for an application.
 *
 * @extends M.Object
 */
M.Application = M.Object.extend(
/** @scope M.Application.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.Application',

    /**
     * The application's name.
     *
     * @type String
     */
    name: null,

    /**
     * The application's view manager.
     *
     * @type Object
     */
    viewManager: M.ViewManager,

    /**
     * The application's model registry.
     *
     * @type Object
     */
    modelRegistry: M.ModelRegistry,

    /**
     * The application's event dispatcher.
     *
     * @type Object
     */
    eventDispatcher: M.EventDispatcher,

    /**
     * The application's cypher object, used for encoding and decoding.
     *
     * @type Object
     */
    cypher: M.Cypher,

    /**
     * The application's current language.
     *
     * @type String
     */
    currentLanguage: null,

    /**
     * The application's default / fallback language.
     *
     * @type String
     */
    defaultLanguage: null,

    /**
     * This property determines whether to use transitions within the application, e.g. on
     * page switches, or not. If set to NO, there will be no framework-sided transitions.
     *
     * @type Boolean
     */
    useTransitions: YES,

    /**
     * This method encapsulates the 'include' method of M.Object for better reading of code syntax.
     * Basically it integrates the defined pages within the application into M.Application and sets
     * some basic configuration properties, e.g. the default language.
     *
     * @param {Object} obj The mixed in object for the extend call.
     */
    design: function(obj) {
        this.include({
            pages: obj
        });
        return this;
    },

    /**
     * The application's main-method, that is called automatically on load of the app.
     * Inside this method the rendering is initiated and all pages are bound to the 'pageshow'
     * event so one can do some action whenever a page is loaded.
     */
    main: function() {
        var that = this;

        /* live is jQuery fn that binds an event to all elements matching a certain selector now and in the future */
        var eventList = 'click change keyup focus blur orientationchange';
        $('*[id]').live(eventList, function(evt) {
            that.eventDispatcher.eventDidHappen(evt);
        });

        /* also bind the orientationchange event to the body of the application */
        $('body').bind('orientationchange', function(evt) {
            that.eventDispatcher.eventDidHappen(evt);
        });

        var html = '';
        for(i in this.viewManager.viewList) {
            if(this.viewManager.viewList[i].type === 'M.PageView') {
                html += this.viewManager.viewList[i].render();
                /* bind the pageshow event to any view's pageDidLoad property function */
                $('#' + this.viewManager.viewList[i].id).bind('pagebeforeshow', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageWillLoad));

                /* bind the pageshow event to any view's pageWillLoad property function */
                $('#' + this.viewManager.viewList[i].id).bind('pageshow', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageDidLoad));

                /* bind the pagebeforehide event to any view's pageWillHide property function */
                $('#' + this.viewManager.viewList[i].id).bind('pagebeforehide', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageWillHide));

                /* bind the pagehide event to any view's pageDidHide property function */
                $('#' + this.viewManager.viewList[i].id).bind('pagehide', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageDidHide));

                /* set the first page as current page to be displayed */
                if(!this.viewManager.getCurrentPage()) {
                    this.viewManager.setCurrentPage(this.viewManager.viewList[i]);
                }
            }
        }
    }

});<commit_msg>Added isFirstLoad property to determine whether a page load is the 'overall fist page load' to do basic initial things<commit_after>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Sebastian
// Date:      02.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/view_manager.js');

/**
 * @class
 *
 * The root class for an application.
 *
 * @extends M.Object
 */
M.Application = M.Object.extend(
/** @scope M.Application.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.Application',

    /**
     * The application's name.
     *
     * @type String
     */
    name: null,

    /**
     * The application's view manager.
     *
     * @type Object
     */
    viewManager: M.ViewManager,

    /**
     * The application's model registry.
     *
     * @type Object
     */
    modelRegistry: M.ModelRegistry,

    /**
     * The application's event dispatcher.
     *
     * @type Object
     */
    eventDispatcher: M.EventDispatcher,

    /**
     * The application's cypher object, used for encoding and decoding.
     *
     * @type Object
     */
    cypher: M.Cypher,

    /**
     * The application's current language.
     *
     * @type String
     */
    currentLanguage: null,

    /**
     * The application's default / fallback language.
     *
     * @type String
     */
    defaultLanguage: null,

    /**
     * This property determines whether to use transitions within the application, e.g. on
     * page switches, or not. If set to NO, there will be no framework-sided transitions.
     *
     * @type Boolean
     */
    useTransitions: YES,

    /**
     * This property is set to NO once the first page within an application was loaded. So this
     * can be used as a hook to trigger some actions at the first load of any view. To do initial
     * things for a specific view, use the isFirstLoad property of M.PageView.
     *
     * @type Boolean
     */
    isFirstLoad: YES,

    /**
     * This method encapsulates the 'include' method of M.Object for better reading of code syntax.
     * Basically it integrates the defined pages within the application into M.Application and sets
     * some basic configuration properties, e.g. the default language.
     *
     * @param {Object} obj The mixed in object for the extend call.
     */
    design: function(obj) {
        this.include({
            pages: obj
        });
        return this;
    },

    /**
     * The application's main-method, that is called automatically on load of the app.
     * Inside this method the rendering is initiated and all pages are bound to the 'pageshow'
     * event so one can do some action whenever a page is loaded.
     */
    main: function() {
        var that = this;

        /* live is jQuery fn that binds an event to all elements matching a certain selector now and in the future */
        var eventList = 'click change keyup focus blur orientationchange';
        $('*[id]').live(eventList, function(evt) {
            that.eventDispatcher.eventDidHappen(evt);
        });

        /* also bind the orientationchange event to the body of the application */
        $('body').bind('orientationchange', function(evt) {
            that.eventDispatcher.eventDidHappen(evt);
        });

        var html = '';
        for(i in this.viewManager.viewList) {
            if(this.viewManager.viewList[i].type === 'M.PageView') {
                html += this.viewManager.viewList[i].render();
                /* bind the pageshow event to any view's pageDidLoad property function */
                $('#' + this.viewManager.viewList[i].id).bind('pagebeforeshow', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageWillLoad));

                /* bind the pageshow event to any view's pageWillLoad property function */
                $('#' + this.viewManager.viewList[i].id).bind('pageshow', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageDidLoad));

                /* bind the pagebeforehide event to any view's pageWillHide property function */
                $('#' + this.viewManager.viewList[i].id).bind('pagebeforehide', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageWillHide));

                /* bind the pagehide event to any view's pageDidHide property function */
                $('#' + this.viewManager.viewList[i].id).bind('pagehide', this.bindToCaller(this.viewManager.viewList[i], this.viewManager.viewList[i].pageDidHide));

                /* set the first page as current page to be displayed */
                if(!this.viewManager.getCurrentPage()) {
                    this.viewManager.setCurrentPage(this.viewManager.viewList[i]);
                }
            }
        }
    }

});<|endoftext|>


<commit_before>'use strict'

const bodyParser = require('body-parser')
const slack = require('slapp/client')

const ConvoStore = require('./convo-store')

var http = require("https");
var querystring = require('querystring');

var attachments = [{
    fallback: "Attachment 1 Fallback",
    title: "This is Attachment 1",
    text: "Attachment 1 Text",
    color: "#3964db"
}, {
    fallback: "Attachment 2 Fallback",
    title: "This is Attachment 2",
    text: "Attachment 2 Text",
    color: "#3964db"
}];

module.exports = (server, db) => {
  let clientId = process.env.SLACK_CLIENT_ID
  let clientSecret = process.env.SLACK_CLIENT_SECRET
  let domain = process.env.DOMAIN

  // let app = Slapp({
  //   verify_token: process.env.SLACK_VERIFY_TOKEN,
  //   context: Context(),
  //   convo_store: ConvoStore()
  // })

  server.get('/cron', (req, res) => {

    let params = {
      client_id: clientId,
      client_secret: clientSecret,
      redirect_uri: `https://${domain || req.get('host')}/cron`
    }

    slack.oauth.access(params, (err, oauthAccess) => {
      if (err) {
        console.error(err)
        return res.status(500).send(err.message || err)
      }

      var url = 'https://slack.com/api/chat.postMessage?user=' + oauthAccess + '&token=' + process.env.SLACK_CLIENT_SECRET + '&channel=@C0320RUB4&text=HelloWorld'

      var message = {
        token: oauthAccess,
        channel: "@C0320RUB4",
        text: "This is a message with attachments"
      }

      var qs = querystring.stringify(message);

      var options = {
        "method": "GET",
        "hostname": "slack.com",
        "path": "/api/chat.postMessage?" + qs
      };

      var req = http.request(options, function (res) {
        var chunks = [];

        res.on("data", function (chunk) {
          chunks.push(chunk);
        });

        res.on("end", function () {
          var body = Buffer.concat(chunks);
          console.log(body.toString());
        });
      });
    })
  })

  server.get('/add', (req, res) => {
    console.log('add')

    db.saveConvo(3, {
        'column':'value',
        'key2': 'value2'
      }, (err, convo) => {
      console.log(err)
    })

  })

  // Landing page w/ `Add to Slack` button
  server.get('/', (req, res) => {

    let redirectURI = `https://${domain || req.get('host')}/add-to-slack`

    res.send(`

Slapp v2 Firebase Slack App

    `)
  })

  // OAuth receiver - exhange, verify and store tokens & team data
  server.get('/add-to-slack', bodyParser.json(), (req, res) => {

    let params = {
      code: req.query.code,
      client_id: clientId,
      client_secret: clientSecret,
      redirect_uri: `https://${domain || req.get('host')}/add-to-slack`
    }

    slack.oauth.access(params, (err, oauthAccess) => {
      if (err) {
        console.error(err)
        return res.status(500).send(err.message || err)
      }

      // test access token
      slack.auth.test({ token: oauthAccess.access_token }, (err, authTest) => {
        if (err) {
          console.error(err)
          return res.status(500).send(err.message || err)
        }

        if (!authTest.team_id) {
          return res.status(500).send('No team_id present in auth.test response')
        }

        db.saveTeam(authTest.team_id, oauthAccess, (err) => {
          if (err) {
            console.error(err)
            return res.status(500).send(err.message || err)
          }

          console.log('Team added: %s', authTest.team_id)
          res.send(`

Slapp Firebase Slack App

This Slack App has been successfully added to the ${authTest.team} team

          `)
        })
      })
    })
  })
}
<commit_msg>Better url.<commit_after>'use strict'

const bodyParser = require('body-parser')
const slack = require('slapp/client')

const ConvoStore = require('./convo-store')

var http = require("https");
var querystring = require('querystring');

var attachments = [{
    fallback: "Attachment 1 Fallback",
    title: "This is Attachment 1",
    text: "Attachment 1 Text",
    color: "#3964db"
}, {
    fallback: "Attachment 2 Fallback",
    title: "This is Attachment 2",
    text: "Attachment 2 Text",
    color: "#3964db"
}];

module.exports = (server, db) => {
  let clientId = process.env.SLACK_CLIENT_ID
  let clientSecret = process.env.SLACK_CLIENT_SECRET
  let domain = process.env.DOMAIN

  // let app = Slapp({
  //   verify_token: process.env.SLACK_VERIFY_TOKEN,
  //   context: Context(),
  //   convo_store: ConvoStore()
  // })

  server.get('/cron', (req, res) => {

    let params = {
      client_id: clientId,
      client_secret: clientSecret,
      redirect_uri: `https://${domain || req.get('host')}/cron`
    }

    slack.oauth.access(params, (err, oauthAccess) => {
      if (err) {
        console.error(err)
        return res.status(500).send(err.message || err)
      }

      var url = 'https://slack.com/api/chat.postMessage?user=token=' + oauthAccess + '&channel=@C0320RUB4&text=HelloWorld'

      var message = {
        token: oauthAccess,
        channel: "@C0320RUB4",
        text: "This is a message with attachments"
      }

      var qs = querystring.stringify(message);

      var options = {
        "method": "GET",
        "hostname": "slack.com",
        "path": "/api/chat.postMessage?" + qs
      };

      var req = http.request(options, function (res) {
        var chunks = [];

        res.on("data", function (chunk) {
          chunks.push(chunk);
        });

        res.on("end", function () {
          var body = Buffer.concat(chunks);
          console.log(body.toString());
        });
      });
    })
  })

  server.get('/add', (req, res) => {
    console.log('add')

    db.saveConvo(3, {
        'column':'value',
        'key2': 'value2'
      }, (err, convo) => {
      console.log(err)
    })

  })

  // Landing page w/ `Add to Slack` button
  server.get('/', (req, res) => {

    let redirectURI = `https://${domain || req.get('host')}/add-to-slack`

    res.send(`

Slapp v2 Firebase Slack App

    `)
  })

  // OAuth receiver - exhange, verify and store tokens & team data
  server.get('/add-to-slack', bodyParser.json(), (req, res) => {

    let params = {
      code: req.query.code,
      client_id: clientId,
      client_secret: clientSecret,
      redirect_uri: `https://${domain || req.get('host')}/add-to-slack`
    }

    slack.oauth.access(params, (err, oauthAccess) => {
      if (err) {
        console.error(err)
        return res.status(500).send(err.message || err)
      }

      // test access token
      slack.auth.test({ token: oauthAccess.access_token }, (err, authTest) => {
        if (err) {
          console.error(err)
          return res.status(500).send(err.message || err)
        }

        if (!authTest.team_id) {
          return res.status(500).send('No team_id present in auth.test response')
        }

        db.saveTeam(authTest.team_id, oauthAccess, (err) => {
          if (err) {
            console.error(err)
            return res.status(500).send(err.message || err)
          }

          console.log('Team added: %s', authTest.team_id)
          res.send(`

Slapp Firebase Slack App

This Slack App has been successfully added to the ${authTest.team} team

          `)
        })
      })
    })
  })
}
<|endoftext|>


<commit_before>'use strict'

const Slapp = require('slapp')
const Context = require('slapp-context-beepboop')
const ConvoStore = require('slapp-convo-beepboop')
var Helper = require('./helper.js')

var inspire_routes = [
    'color_route',
    'space_route'
  ];

module.exports = (server, db) => {
  let app = Slapp({
    verify_token: process.env.SLACK_VERIFY_TOKEN,
    context: Context(),
    convo_store: ConvoStore()
  })

  // Middleware
  app.use((msg, next) => {
    if (msg.body.event.user)
    {
      // Save message sent to Creature-bot
      db.saveConvo(msg.body.event.user, msg.body, (err, convo) => {
        if (err)
        {
          console.log(err)
        }
      })
      // Increase user experience
      db.increaseExperience(msg.body.event.user, (err) => {
        if (err)
        {
          console.log(err)
        }
      })
    }
    next()
  })

  var HELP_TEXT = `
  I will respond to the following messages:
  \`help\` - to see this message
  \`hi\` - to demonstrate a conversation that tracks state.
  \`thanks\` - to demonstrate a simple response.
  \`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
  \`attachment\` - to see a Slack attachment message.
  `

//*********************************************
// Setup different handlers for messages
//*********************************************

  app.message('inspire', ['direct_mention', 'direct_message'], (msg, text) => {
    var random_index = Math.floor(Math.random() * inspire_routes.length)
    var route_choosen = inspire_routes[random_index]
    console.log(random_index)
    console.log(route_choosen)
    msg.route(route_choosen)
  })

  //*********************************************
  // Begin Color Name Game
  //*********************************************
  app.route('color_route', (msg, text) => {
    var letters = '0123456789ABCDEF'
    var color = ''
    for (var i = 0; i < 6; i++) {
      color += letters[Math.floor(Math.random() * 16)]
    }
    var color_image_url = 'https://dummyimage.com/100x100/' + color + '/' + color + '.jpg'
    msg.say('Give this color a unique name?')
      .say({
        text: '',
        "attachments": [
          {
              "fallback": color,
              "color": color,
              "pretext": '',
              "title": '',
              "title_link": '',
              "text": '',
              "image_url": color_image_url,
              "thumb_url": color_image_url,
          }
        ]
      })
      .route('color_response')
  })

  app.route('color_response', (msg, text) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])
  })
  //*********************************************
  // End Color Name Game
  //*********************************************

  //*********************************************
  // Begin Space Image Game
  //*********************************************
  app.route('space_route', (msg, text) => {
    // Code used to inject space images and facts.
    // db.saveSpaceImages( {
    //   'title': "Core of the Crab Nebula",
    //   'text': 'Peering deep into the core of the Crab Nebula, this close-up image reveals the beating heart of one of the most historic and intensively studied remnants of a supernova, an exploding star.',
    //   'image': 'https://media.stsci.edu/uploads/image/display_image/3760/low_xlarge_web.jpg',
    // },(err) => {
    //     if (err) {
    //       console.error(err)
    //       msg.say(err)
    //     }
    // })
    // Pull from database space images and captions
    db.getRandomSpaceImage((err, spaceImage) => {
        if (err) {
          console.error(err)
        }
        // Setup the attachment for the response
        msg.say({
          text: '',
          "attachments": [
            {
                "fallback": spaceImage.title,
                "color": "#36a64f",
                "pretext": "Here is an image of something in space.  Tell me some words or phrases that come to mind when you look at this picture?",
                "author_name": "",
                "author_link": "http://flickr.com/bobby/",
                "author_icon": "http://flickr.com/icons/bobby.jpg",
                "title": spaceImage.title,
                "title_link": "",
                "text": spaceImage.text,
                "image_url": spaceImage.image,
                "thumb_url": spaceImage.image,
            }
          ]
        })
        .route('space_response')
    })
  })

  app.route('space_response', (msg, state) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])

  })
  //*********************************************
  // End Space Image Game
  //*********************************************

  app
    .message('motivators', ['direct_mention', 'direct_message'], (msg, text) => {

      var motivators

      db.getMotivations(msg.body.event.user, (err, motivations) => {
        if (err) {
          console.error(err)
        }

        motivators = motivations
        if (motivators instanceof Object)
        {
          msg
            .say('You have motivations set.')
            .say(`Do more of this: `+motivators.motivates+'. Do less of this: '+motivators.discourages)
        }
        else
        {
          msg
            .say(`What is something that motivates you to create?`)
            // sends next event from user to this route, passing along state
            .route('motivates')
        }
      })
    })
  app.route('motivates', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear a response.")
          .say('What is something that motivates you to create?')
          .route('motivates', state)
      }

      // add their response to state
      state.motivates = text

      msg
        .say(`Ok. And what's something that discourages you from creating?`)
        .route('discourages', state)
    })
  app.route('discourages', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear something that discourages you from creating.")
          .route('discourages', state)
      }

      // add their response to state
      state.discourages = text

      // Store the responses
      db.saveMotivations(msg.body.event.user, { 'motivates' : state.motivates , 'discourages' : state.discourages }, (err, convo) => {
        console.log(err)
      })

      msg
        .say('Thanks for sharing.')
        .say(`Do more of this: `+state.motivates+'. Do less of this: '+state.discourages)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // response to the user typing "help"
  app.message('help', ['mention', 'direct_message'], (msg) => {
    msg.say(HELP_TEXT)
  })

  // Test response for boxes
  app.message('yesno', (msg) => {
    msg.say({
      text: '',
      attachments: [
        {
          text: '',
          fallback: 'Yes or No?',
          callback_id: 'yesno_callback',
          actions: [
            { name: 'answer', text: 'Yes', type: 'button', value: 'yes' },
            { name: 'answer', text: 'No',  type: 'button',  value: 'no' }
          ]
        }]
      })
  })

  app.action('yesno_callback', 'answer', (msg, value) => {
    msg.respond(msg.body.response_url, `${value} is a good choice!`)
  })

  // "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(`${text}, how are you?`)
        // sends next event from user to this route, passing along state
        .route('how-are-you', { greeting: text })
    })
    .route('how-are-you', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear how you're doing.")
          .say('How are you?')
          .route('how-are-you', state)
      }

      // add their response to state
      state.status = text

      msg
        .say(`Ok then. What's your favorite color?`)
        .route('color', state)
    })
    .route('color', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear your favorite color.")
          .route('color', state)
      }

      // add their response to state
      state.color = text

      msg
        .say('Thanks for sharing.')
        .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // Can use a regex as well
  app.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
    // You can provide a list of responses, and a random one will be chosen
    // You can also include slack emoji in your responses
    msg.say([
      "You're welcome :smile:",
      'You bet',
      ':+1: Of course',
      'Anytime :sun_with_face: :full_moon_with_face:'
    ])
  })

  // demonstrate returning an attachment...
  app.message('attachment', ['mention', 'direct_message'], (msg) => {
    msg.say({
      text: 'Check out this amazing attachment! :confetti_ball: ',
      attachments: [{
        text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
        title: 'Slapp Library - Open Source',
        image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
        title_link: 'https://beepboophq.com/',
        color: '#7CD197'
      }]
    })
  })

  // response to the user asking "how did you get here?"
  app.message('how did you get here?', ['mention', 'direct_message'], (msg) => {
    msg.say('Team Rocket! :rocket:')
  })

  // "Creative motivators" flow

  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(text + ', how are you?')
        .route('how-are-you', { text }, 60)
    })
    .route('how-are-you', (msg, state) => {
      var resp = msg.body.event && msg.body.event.text

      if (/good/i.test(resp)) {
        msg
          .say(['Great! Ready?', ':smile: Are you sure?'])
          .route('how-are-you', state, 60)
      } else {
        msg
          .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))
      }
    })
    .message('^(thanks|thank you)', ['mention', 'direct_message'], (msg) => {
      msg.say(["You're welcome", 'Of course'])
    })
    .message('help', ['direct_message', 'direct_mention'], (msg) => {
      let help = `I will respond to the following messages:
\`@slapp hi||hello\` for a greeting.
\`mention me by name in a message\` to demonstrate detecting a mention.
\`@slapp help\` to see this again.`

      msg.say(help)
    })
    .message('fix it', 'ambient', (msg) => {
      msg.say('https://www.youtube.com/watch?v=yo3uxqwTxk0')
    })
    // .message(/.*/, 'mention', (msg) => {
    //   msg.say('You really do care about me. :heart:')
    // })

    app.attachToExpress(server)

  return app
}
<commit_msg>Adds direct route for space game.<commit_after>'use strict'

const Slapp = require('slapp')
const Context = require('slapp-context-beepboop')
const ConvoStore = require('slapp-convo-beepboop')
var Helper = require('./helper.js')

var inspire_routes = [
    'color_route',
    'space_route'
  ];

module.exports = (server, db) => {
  let app = Slapp({
    verify_token: process.env.SLACK_VERIFY_TOKEN,
    context: Context(),
    convo_store: ConvoStore()
  })

  //*********************************************
  // Middleware
  //*********************************************
  app.use((msg, next) => {
    if (msg.body.event.user)
    {
      // Save message sent to Creature-bot
      db.saveConvo(msg.body.event.user, msg.body, (err, convo) => {
        if (err)
        {
          console.log(err)
        }
      })
      // Increase user experience
      db.increaseExperience(msg.body.event.user, (err) => {
        if (err)
        {
          console.log(err)
        }
      })
    }
    next()
  })

  var HELP_TEXT = `
  I will respond to the following messages:
  \`help\` - to see this message
  \`hi\` - to demonstrate a conversation that tracks state.
  \`thanks\` - to demonstrate a simple response.
  \`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
  \`attachment\` - to see a Slack attachment message.
  `

//*********************************************
// Setup different handlers for messages
//*********************************************

  //*********************************************
  // Inspire Handler
  //*********************************************
  app.message('inspire', ['direct_mention', 'direct_message'], (msg, text) => {
    var random_index = Math.floor(Math.random() * inspire_routes.length)
    var route_choosen = inspire_routes[random_index]
    console.log(random_index)
    console.log(route_choosen)
    msg.route(route_choosen)
  })

  //*********************************************
  // Begin Color Name Game
  //*********************************************
  app.route('color_route', (msg, text) => {
    var letters = '0123456789ABCDEF'
    var color = ''
    for (var i = 0; i < 6; i++) {
      color += letters[Math.floor(Math.random() * 16)]
    }
    var color_image_url = 'https://dummyimage.com/100x100/' + color + '/' + color + '.jpg'
    msg.say('Give this color a unique name?')
      .say({
        text: '',
        "attachments": [
          {
              "fallback": color,
              "color": color,
              "pretext": '',
              "title": '',
              "title_link": '',
              "text": '',
              "image_url": color_image_url,
              "thumb_url": color_image_url,
          }
        ]
      })
      .route('color_response')
  })

  app.route('color_response', (msg, text) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])
  })
  //*********************************************
  // End Color Name Game
  //*********************************************

  //*********************************************
  // Begin Space Image Game
  //*********************************************
  app.message('space', ['direct_mention', 'direct_message'], (msg, text) => {
    msg.route('space_route')
  })

  app.route('space_route', (msg, text) => {
    // Code used to inject space images and facts.
    // db.saveSpaceImages( {
    //   'title': "Core of the Crab Nebula",
    //   'text': 'Peering deep into the core of the Crab Nebula, this close-up image reveals the beating heart of one of the most historic and intensively studied remnants of a supernova, an exploding star.',
    //   'image': 'https://media.stsci.edu/uploads/image/display_image/3760/low_xlarge_web.jpg',
    // },(err) => {
    //     if (err) {
    //       console.error(err)
    //       msg.say(err)
    //     }
    // })
    // Pull from database space images and captions
    db.getRandomSpaceImage((err, spaceImage) => {
        if (err) {
          console.error(err)
        }
        // Setup the attachment for the response
        msg.say({
          text: '',
          "attachments": [
            {
                "fallback": spaceImage.title,
                "color": "#36a64f",
                "pretext": "Here is an image of something in space.  Tell me some words or phrases that come to mind when you look at this picture?",
                "author_name": "",
                "author_link": "http://flickr.com/bobby/",
                "author_icon": "http://flickr.com/icons/bobby.jpg",
                "title": spaceImage.title,
                "title_link": "",
                "text": spaceImage.text,
                "image_url": spaceImage.image,
                "thumb_url": spaceImage.image,
            }
          ]
        })
        .route('space_response')
    })
  })

  app.route('space_response', (msg, state) => {
    msg.say(['Thanks, I have taken note.', 'Sounds good, I am keeping track of these.', 'Thanks, keep up the good work.'])

  })
  //*********************************************
  // End Space Image Game
  //*********************************************

  app
    .message('motivators', ['direct_mention', 'direct_message'], (msg, text) => {

      var motivators

      db.getMotivations(msg.body.event.user, (err, motivations) => {
        if (err) {
          console.error(err)
        }

        motivators = motivations
        if (motivators instanceof Object)
        {
          msg
            .say('You have motivations set.')
            .say(`Do more of this: `+motivators.motivates+'. Do less of this: '+motivators.discourages)
        }
        else
        {
          msg
            .say(`What is something that motivates you to create?`)
            // sends next event from user to this route, passing along state
            .route('motivates')
        }
      })
    })
  app.route('motivates', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear a response.")
          .say('What is something that motivates you to create?')
          .route('motivates', state)
      }

      // add their response to state
      state.motivates = text

      msg
        .say(`Ok. And what's something that discourages you from creating?`)
        .route('discourages', state)
    })
  app.route('discourages', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear something that discourages you from creating.")
          .route('discourages', state)
      }

      // add their response to state
      state.discourages = text

      // Store the responses
      db.saveMotivations(msg.body.event.user, { 'motivates' : state.motivates , 'discourages' : state.discourages }, (err, convo) => {
        console.log(err)
      })

      msg
        .say('Thanks for sharing.')
        .say(`Do more of this: `+state.motivates+'. Do less of this: '+state.discourages)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // response to the user typing "help"
  app.message('help', ['mention', 'direct_message'], (msg) => {
    msg.say(HELP_TEXT)
  })

  // Test response for boxes
  app.message('yesno', (msg) => {
    msg.say({
      text: '',
      attachments: [
        {
          text: '',
          fallback: 'Yes or No?',
          callback_id: 'yesno_callback',
          actions: [
            { name: 'answer', text: 'Yes', type: 'button', value: 'yes' },
            { name: 'answer', text: 'No',  type: 'button',  value: 'no' }
          ]
        }]
      })
  })

  app.action('yesno_callback', 'answer', (msg, value) => {
    msg.respond(msg.body.response_url, `${value} is a good choice!`)
  })

  // "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(`${text}, how are you?`)
        // sends next event from user to this route, passing along state
        .route('how-are-you', { greeting: text })
    })
    .route('how-are-you', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear how you're doing.")
          .say('How are you?')
          .route('how-are-you', state)
      }

      // add their response to state
      state.status = text

      msg
        .say(`Ok then. What's your favorite color?`)
        .route('color', state)
    })
    .route('color', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear your favorite color.")
          .route('color', state)
      }

      // add their response to state
      state.color = text

      msg
        .say('Thanks for sharing.')
        .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // Can use a regex as well
  app.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
    // You can provide a list of responses, and a random one will be chosen
    // You can also include slack emoji in your responses
    msg.say([
      "You're welcome :smile:",
      'You bet',
      ':+1: Of course',
      'Anytime :sun_with_face: :full_moon_with_face:'
    ])
  })

  // demonstrate returning an attachment...
  app.message('attachment', ['mention', 'direct_message'], (msg) => {
    msg.say({
      text: 'Check out this amazing attachment! :confetti_ball: ',
      attachments: [{
        text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
        title: 'Slapp Library - Open Source',
        image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
        title_link: 'https://beepboophq.com/',
        color: '#7CD197'
      }]
    })
  })

  // response to the user asking "how did you get here?"
  app.message('how did you get here?', ['mention', 'direct_message'], (msg) => {
    msg.say('Team Rocket! :rocket:')
  })

  // "Creative motivators" flow

  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(text + ', how are you?')
        .route('how-are-you', { text }, 60)
    })
    .route('how-are-you', (msg, state) => {
      var resp = msg.body.event && msg.body.event.text

      if (/good/i.test(resp)) {
        msg
          .say(['Great! Ready?', ':smile: Are you sure?'])
          .route('how-are-you', state, 60)
      } else {
        msg
          .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))
      }
    })
    .message('^(thanks|thank you)', ['mention', 'direct_message'], (msg) => {
      msg.say(["You're welcome", 'Of course'])
    })
    .message('help', ['direct_message', 'direct_mention'], (msg) => {
      let help = `I will respond to the following messages:
\`@slapp hi||hello\` for a greeting.
\`mention me by name in a message\` to demonstrate detecting a mention.
\`@slapp help\` to see this again.`

      msg.say(help)
    })
    .message('fix it', 'ambient', (msg) => {
      msg.say('https://www.youtube.com/watch?v=yo3uxqwTxk0')
    })
    // .message(/.*/, 'mention', (msg) => {
    //   msg.say('You really do care about me. :heart:')
    // })

    app.attachToExpress(server)

  return app
}
<|endoftext|>


<commit_before>/*
This module provides functions and classes for streaming video all over town.

Highlights include:

### CT.stream.record(ondata, onrecorder, onfail)
Starts up a video stream from your webcam. Breaks stream into segments as
defined by CT.stream.opts.chunk (default 1000) and CT.stream.opts.segments
(default 10).

### CT.stream.Multiplexer
Uses WebSocket pubsub server (ctpubsub/CT.pubsub) to manage multiple channels,
each streaming metadata pertaining to multiple videos, over a single connection.
The video segments are acquired from the server with the CT.memcache module
and passed to individual CT.stream.Video instances.

### CT.stream.Video
Wraps an HTML5 video tag in a class with functions for streaming a video
that arrives in chunks.
*/

CT.require("CT.stream.util");
CT.require("CT.stream.Audio");
CT.require("CT.stream.Video");
CT.require("CT.stream.Streamer");
CT.require("CT.stream.Multiplexer");

CT.stream.opts = {
	requiresInput: CT.info.android,
	requestedInput: false,
	segments: 10,
	chunk: 1000,
	reset: 3000,
	cutoff: 30000,
	width: 320,
	height: 240,
	waiting: [],
	transcoder: null, // set to func, such as web hook procedure
	setTranscoder: function(cb) {
		CT.stream.opts.transcoder = cb;
	},
	codecs: {
		av: 'video/webm; codecs="vp9,opus"',
		video: 'video/webm; codecs="vp9"',
		audio: 'audio/webm; codecs="opus"'
	},
	startWaiting: function() {
		CT.stream.opts.waiting.forEach(function(w) { w.play(); });
	}
};
CT.stream.opts.mropts = {
	mimeType: CT.stream.opts.codecs.video, // for OSX
	videoBitsPerSecond: 64000,
	audioBitsPerSecond: 32000
};<commit_msg>stream: increased video bitrate to 128k (chrome now seems to enforce a 100k minimum)<commit_after>/*
This module provides functions and classes for streaming video all over town.

Highlights include:

### CT.stream.record(ondata, onrecorder, onfail)
Starts up a video stream from your webcam. Breaks stream into segments as
defined by CT.stream.opts.chunk (default 1000) and CT.stream.opts.segments
(default 10).

### CT.stream.Multiplexer
Uses WebSocket pubsub server (ctpubsub/CT.pubsub) to manage multiple channels,
each streaming metadata pertaining to multiple videos, over a single connection.
The video segments are acquired from the server with the CT.memcache module
and passed to individual CT.stream.Video instances.

### CT.stream.Video
Wraps an HTML5 video tag in a class with functions for streaming a video
that arrives in chunks.
*/

CT.require("CT.stream.util");
CT.require("CT.stream.Audio");
CT.require("CT.stream.Video");
CT.require("CT.stream.Streamer");
CT.require("CT.stream.Multiplexer");

CT.stream.opts = {
	requiresInput: CT.info.android,
	requestedInput: false,
	segments: 10,
	chunk: 1000,
	reset: 3000,
	cutoff: 30000,
	width: 320,
	height: 240,
	waiting: [],
	transcoder: null, // set to func, such as web hook procedure
	setTranscoder: function(cb) {
		CT.stream.opts.transcoder = cb;
	},
	codecs: {
		av: 'video/webm; codecs="vp9,opus"',
		video: 'video/webm; codecs="vp9"',
		audio: 'audio/webm; codecs="opus"'
	},
	startWaiting: function() {
		CT.stream.opts.waiting.forEach(function(w) { w.play(); });
	}
};
CT.stream.opts.mropts = {
	mimeType: CT.stream.opts.codecs.video, // for OSX
	videoBitsPerSecond: 128000,
	audioBitsPerSecond: 32000
};<|endoftext|>


<commit_before>// call: waitress("#main", { fontSize: "20px" }, function(){});
// or: waitress("p", addIndent()); ...etc...
var waitress = function(master, job, support) {
  // wait for master(s) then do job and if support do support once
  var sortie = master.split(', ');
  var work = (typeof (job) === "function");

  waitress.style = function(parent, name) {
    // this function operates with <style> tags. applies to masters of kind class or tag

    var assign = function() { return document.getElementsByName(name + "_style")[0]; };
    if(!assign()) { parent.insertAdjacentHTML('beforeend', "<style name='" + name + "_style'></style>"); }
    // create new style tag or get existant
    var tag = assign();

    var hyphenize = function(prop) {
      // converts fontSize into font-size etc
      return prop.replace(/[A-Z]/g, function(m, o) { return (o ? '-' : '') + m.toLowerCase(); });
    };

    var structurize = function(el, rules) {
      var joint = '', last = Object.keys(rules)[Object.keys(rules).length - 1];
      // construct css rule (aka #foo{bar: baz;}) from waitress('#foo', { bar: 'baz' });
      for (var i in rules) {
        if (!rules.hasOwnProperty(i)) { continue; }
        var key = hyphenize(String(i)), value = String(rules[i]);
        joint += key + ':' + ' ' + value + (i == last ? '' : ';');
      }

      return (el + '{' + joint + '}');
    };

    var rebuild = function(arr) {
      var rulesArray = [], i = 0;
      // operate only rules in tag with the same identifier|classifier as function ident
      for(i = arr.length - 1; i >= 0; i--) {
        if(name === arr[i].split(/{|}/)[0]) {
          rulesArray = rulesArray.concat(arr[i].split(/{|}/)[1].split(/;/).filter(String));
          arr.splice(i, 1);
        }
      }
      // clean whitespaces
      for(i = 0; i < rulesArray.length; i++) { rulesArray[i] = rulesArray[i].trim(); }
      // delete old rules with the same key as new rules, but leave other intact
      Object.keys(job).forEach(function(key) {
        for(i = 0; i < rulesArray.length; i++) {
          if(rulesArray[i].includes(hyphenize(String(key)))) {
            rulesArray.splice(i, 1);
          }
        }
      });
      // add back what is left (aka delete rules with the same key as in 'css' object)
      tag.innerText = arr.join('') + name + '{' + rulesArray.join(';') + '}';
    };

    var tagRules = tag.innerText.replace(/}/g, '}___').split('___').filter(String);
    // if style tag has rules check if they don't overlap with new ones
    if(tagRules.length > 0) { rebuild(tagRules); }

    // add new rules
    tag.innerText += structurize(name.toString(), job);
  };

  waitress.act = function(action) {
    if(work) { job(); }
    if(!work) { action(); }
    if(support && !waitress.supported) { support(); waitress.supported = true; }
  };

  waitress.wait = function(one) {
    if(((/(:before|:after)$/.test(one))
      && ((one.split(/(:before|:after)$/)[0].startsWith('.', 0)
        && document.getElementsByClassName(one.substr(1).split(/(:before|:after)$/)[0])[0])
      || (one.split(/(:before|:after)$/)[0].startsWith('#', 0)
        && document.getElementById(one.substr(1).split(/(:before|:after)$/)[0]))
      || (/^[a-z]/.test(one)
        && document.getElementsByTagName(one.split(/(:before|:after)$/)[0])[0]))
      )
    || ((/^[a-z]/.test(one)) && (one !== "body") && document.getElementsByTagName(one)[0])
    || (one.toString().startsWith('.', 0) && document.getElementsByClassName(one.substr(1))[0])) {
      // scheme: first check type of name then if corresponding elem exists in doc

      waitress.act(function() { waitress.style(document.head, one); });
    } else if(one.toString().startsWith('#', 0) && document.getElementById(one.substr(1))) {
      waitress.act(function() { Object.assign(document.getElementById(one.substr(1)).style, job); });
    } else if ((one === "body") && document.body) {
      waitress.act(function() { Object.assign(document.body.style, job); });
    } else {
      requestAnimationFrame(function() { waitress(master, job, support); });
    }
  };

  // only two types of job could be done: styling and function
  if(["object", "function"].includes(typeof (job))) {
    for(var i = 0; i < sortie.length; i++) {
      waitress.wait(sortie[i]);
    }
  } else {
    window.console.info("waitress function job is unknown.");
  }
};
<commit_msg>bugfix in waitress when infinite loop with several masters<commit_after>// call: waitress("#main", { fontSize: "20px" }, function(){});
// or: waitress("p", addIndent()); ...etc...
var waitress = function(master, job, support) {
  // wait for master(s) then do job and if support do support once
  var sortie = master.split(', ');
  var work = (typeof (job) === "function");

  waitress.style = function(parent, name) {
    // this function operates with <style> tags. applies to masters of kind class or tag

    var assign = function() { return document.getElementsByName(name + "_style")[0]; };
    if(!assign()) { parent.insertAdjacentHTML('beforeend', "<style name='" + name + "_style'></style>"); }
    // create new style tag or get existant
    var tag = assign();

    var hyphenize = function(prop) {
      // converts fontSize into font-size etc
      return prop.replace(/[A-Z]/g, function(m, o) { return (o ? '-' : '') + m.toLowerCase(); });
    };

    var structurize = function(el, rules) {
      var joint = '', last = Object.keys(rules)[Object.keys(rules).length - 1];
      // construct css rule (aka #foo{bar: baz;}) from waitress('#foo', { bar: 'baz' });
      for (var i in rules) {
        if (!rules.hasOwnProperty(i)) { continue; }
        var key = hyphenize(String(i)), value = String(rules[i]);
        joint += key + ':' + ' ' + value + (i == last ? '' : ';');
      }

      return (el + '{' + joint + '}');
    };

    var rebuild = function(arr) {
      var rulesArray = [], i = 0;
      // operate only rules in tag with the same identifier|classifier as function ident
      for(i = arr.length - 1; i >= 0; i--) {
        if(name === arr[i].split(/{|}/)[0]) {
          rulesArray = rulesArray.concat(arr[i].split(/{|}/)[1].split(/;/).filter(String));
          arr.splice(i, 1);
        }
      }
      // clean whitespaces
      for(i = 0; i < rulesArray.length; i++) { rulesArray[i] = rulesArray[i].trim(); }
      // delete old rules with the same key as new rules, but leave other intact
      Object.keys(job).forEach(function(key) {
        for(i = 0; i < rulesArray.length; i++) {
          if(rulesArray[i].includes(hyphenize(String(key)))) {
            rulesArray.splice(i, 1);
          }
        }
      });
      // add back what is left (aka delete rules with the same key as in 'css' object)
      tag.innerText = arr.join('') + name + '{' + rulesArray.join(';') + '}';
    };

    var tagRules = tag.innerText.replace(/}/g, '}___').split('___').filter(String);
    // if style tag has rules check if they don't overlap with new ones
    if(tagRules.length > 0) { rebuild(tagRules); }

    // add new rules
    tag.innerText += structurize(name.toString(), job);
  };

  waitress.act = function(action) {
    if(work) { job(); }
    if(!work) { action(); }
    if(support && !waitress.supported) { support(); waitress.supported = true; }
  };

  waitress.wait = function(one) {
    if(((/(:before|:after)$/.test(one))
      && ((one.split(/(:before|:after)$/)[0].startsWith('.', 0)
        && document.getElementsByClassName(one.substr(1).split(/(:before|:after)$/)[0])[0])
      || (one.split(/(:before|:after)$/)[0].startsWith('#', 0)
        && document.getElementById(one.substr(1).split(/(:before|:after)$/)[0]))
      || (/^[a-z]/.test(one)
        && document.getElementsByTagName(one.split(/(:before|:after)$/)[0])[0]))
      )
    || ((/^[a-z]/.test(one)) && (one !== "body") && document.getElementsByTagName(one)[0])
    || (one.toString().startsWith('.', 0) && document.getElementsByClassName(one.substr(1))[0])) {
      // scheme: first check type of name then if corresponding elem exists in doc

      waitress.act(function() { waitress.style(document.head, one); });
    } else if(one.toString().startsWith('#', 0) && document.getElementById(one.substr(1))) {
      waitress.act(function() { Object.assign(document.getElementById(one.substr(1)).style, job); });
    } else if ((one === "body") && document.body) {
      waitress.act(function() { Object.assign(document.body.style, job); });
    } else {
      requestAnimationFrame(function() { waitress(one, job, support); });
    }
  };

  // only two types of job could be done: styling and function
  if(["object", "function"].includes(typeof (job))) {
    for(var i = 0; i < sortie.length; i++) {
      waitress.wait(sortie[i]);
    }
  } else {
    window.console.info("waitress function job is unknown.");
  }
};
<|endoftext|>


<commit_before>// call: waitress("#main", { fontSize: "20px" }, function(){});
// or: waitress("p", addIndent()); ...etc...
var waitress = function(master, job, support) {
  // wait for master(s) then do job and if support do support once
  var sortie = master.split(', ');
  var work = (typeof (job) === "function");

  waitress.style = function(parent, name) {
    // this function operates with <style> tags. applies to masters of kind class or tag

    var assign = function() { return document.getElementsByName(name + "_style")[0]; };
    if(!assign()) { parent.insertAdjacentHTML('beforeend', "<style name='" + name + "_style'></style>"); }
    // create new style tag or get existant
    var tag = assign();

    var hyphenize = function(prop) {
      // converts fontSize into font-size etc
      return prop.replace(/[A-Z]/g, function(m, o) { return (o ? '-' : '') + m.toLowerCase(); });
    };

    var structurize = function(el, rules) {
      var joint = '', last = Object.keys(rules)[Object.keys(rules).length - 1];
      // construct css rule (aka #foo{bar: baz;}) from waitress('#foo', { bar: 'baz' });
      for (var i in rules) {
        if (!rules.hasOwnProperty(i)) { continue; }
        var key = hyphenize(String(i)), value = String(rules[i]);
        joint += key + ':' + ' ' + value + (i == last ? '' : ';');
      }

      return (el + '{' + joint + '}');
    };

    var rebuild = function(arr) {
      var rulesArray = [], i = 0;
      // operate only rules in tag with the same identifier|classifier as function ident
      for(i = arr.length - 1; i >= 0; i--) {
        if(name === arr[i].split(/{|}/)[0]) {
          rulesArray = rulesArray.concat(arr[i].split(/{|}/)[1].split(/;/).filter(String));
          arr.splice(i, 1);
        }
      }
      // clean whitespaces
      for(i = 0; i < rulesArray.length; i++) { rulesArray[i] = rulesArray[i].trim(); }
      // delete old rules with the same key as new rules, but leave other intact
      Object.keys(job).forEach(function(key) {
        for(i = 0; i < rulesArray.length; i++) {
          if(rulesArray[i].includes(hyphenize(String(key)))) {
            rulesArray.splice(i, 1);
          }
        }
      });
      // add back what is left (aka delete rules with the same key as in 'css' object)
      tag.innerText = arr.join('') + name + '{' + rulesArray.join(';') + '}';
    };

    var tagRules = tag.innerText.replace(/}/g, '}___').split('___').filter(String);
    // if style tag has rules check if they don't overlap with new ones
    if(tagRules.length > 0) { rebuild(tagRules); }

    // add new rules
    tag.innerText += structurize(name.toString(), job);
  };

  waitress.act = function(action) {
    if(work) { job(); }
    if(!work) { action(); }
    if(support && !waitress.supported) { support(); waitress.supported = true; }
  };

  waitress.wait = function(one) {
    var regex = /(:link|:visited|:hover|:active|:before|::before|:after|::after)$/;
    var clean = one;
    while(regex.test(clean)) { clean = clean.split(regex)[0]; }
    while(/^(#|\.)/.test(clean)) { clean = clean.substr(1); }
    switch(true) {
      case ((one === "body") && (one === clean) && !!document.body):
        waitress.act(function() { Object.assign(document.body.style, job); });
        break;
      case (regex.test(one)):
        // fall through
      case (one.startsWith('.', 0) && !!document.getElementsByClassName(clean)[0]):
        // fall through
      case ((/^[a-z]/.test(one)) && !!document.getElementsByTagName(clean)[0]):
        waitress.act(function() { waitress.style(document.head, one); });
        break;
      case (one.startsWith('#', 0) && !!document.getElementById(clean)):
        waitress.act(function() { Object.assign(document.getElementById(one.substr(1)).style, job); });
        break;
      default:
        window.requestAnimationFrame(function() { waitress(one, job, support); });
    }
  };

  // only two types of job could be done: styling and function
  if(["object", "function"].includes(typeof (job))) {
    for(var i = 0; i < sortie.length; i++) {
      waitress.wait(sortie[i]);
    }
  } else {
    window.console.info("waitress function job is unknown.");
  }
};
<commit_msg>beautify formatting in waitress style func<commit_after>// call: waitress("#main", { fontSize: "20px" }, function(){});
// or: waitress("p", addIndent()); ...etc...
var waitress = function(master, job, support) {
  // wait for master(s) then do job and if support do support once
  var sortie = master.split(', ');
  var work = (typeof (job) === "function");

  waitress.style = function(parent, name) {
    // this function operates with <style> tags. applies to masters of kind class or tag

    var assign = function() { return document.getElementsByName(name + "_style")[0]; };
    if(!assign()) { parent.insertAdjacentHTML('beforeend', "<style name='" + name + "_style'></style>"); }
    // create new style tag or get existant
    var tag = assign();

    var hyphenize = function(prop) {
      // converts fontSize into font-size etc
      return prop.replace(/[A-Z]/g, function(m, o) { return (o ? '-' : '') + m.toLowerCase(); });
    };

    var structurize = function(el, rules) {
      var joint = '', last = Object.keys(rules)[Object.keys(rules).length - 1];
      // construct css rule (aka #foo{bar: baz;}) from waitress('#foo', { bar: 'baz' });
      for (var i in rules) {
        if (!rules.hasOwnProperty(i)) { continue; }
        var key = hyphenize(String(i)), value = String(rules[i]);
        joint += '\n\t' + key + ': ' + value + (i == last ? '' : '; ');
      }

      return (el + ' {' + joint + '\n}');
    };

    var rebuild = function(arr) {
      var rulesArray = [], i = 0;
      // operate only rules in tag with the same identifier|classifier as function ident
      for(i = arr.length - 1; i >= 0; i--) {
        if(name === arr[i].split(/{|}/)[0].trim()) {
          rulesArray = rulesArray.concat(arr[i].split(/{|}/)[1].trim().split(/;/).filter(String));
          arr.splice(i, 1);
        }
      }
      // clean whitespaces
      for(i = 0; i < rulesArray.length; i++) { rulesArray[i] = rulesArray[i].trim(); }
      // delete old rules with the same key as new rules, but leave other intact
      Object.keys(job).forEach(function(key) {
        for(i = 0; i < rulesArray.length; i++) {
          if(rulesArray[i].includes(hyphenize(String(key)))) {
            rulesArray.splice(i, 1);
          }
        }
      });
      // add back what is left (aka delete rules with the same key as in 'css' object)
      tag.innerText = arr.join('\n') + '\n' + name + ' {\n\t' + rulesArray.join(';\n\t') + '\n}';
    };

    var tagRules = tag.innerText.replace(/}/g, '}___').split('___').filter(String);
    // if style tag has rules check if they don't overlap with new ones and remove if yes
    if(tagRules.length > 0) { rebuild(tagRules); }

    // add new rules
    tag.innerText += structurize(name.toString(), job);
  };

  waitress.act = function(action) {
    if(work) { job(); }
    if(!work) { action(); }
    if(support && !waitress.supported) { support(); waitress.supported = true; }
  };

  waitress.wait = function(one) {
    var regex = /(:link|:visited|:hover|:active|:before|::before|:after|::after)$/;
    var clean = one;
    while(regex.test(clean)) { clean = clean.split(regex)[0]; }
    while(/^(#|\.)/.test(clean)) { clean = clean.substr(1); }
    switch(true) {
      case ((one === "body") && (one === clean) && !!document.body):
        waitress.act(function() { Object.assign(document.body.style, job); });
        break;
      case (regex.test(one)):
        // fall through
      case (one.startsWith('.', 0) && !!document.getElementsByClassName(clean)[0]):
        // fall through
      case ((/^[a-z]/.test(one)) && !!document.getElementsByTagName(clean)[0]):
        waitress.act(function() { waitress.style(document.head, one); });
        break;
      case (one.startsWith('#', 0) && !!document.getElementById(clean)):
        waitress.act(function() { Object.assign(document.getElementById(one.substr(1)).style, job); });
        break;
      default:
        window.requestAnimationFrame(function() { waitress(one, job, support); });
    }
  };

  // only two types of job could be done: styling and function
  if(["object", "function"].includes(typeof (job))) {
    for(var i = 0; i < sortie.length; i++) {
      waitress.wait(sortie[i]);
    }
  } else {
    window.console.info("waitress function job is unknown.");
  }
};
<|endoftext|>