<commit_before>define([
    'app',
    'services/event',

], function (app) {
    'use strict';

    app.controller('ChatCtrl', [
        '$scope',
        '$state',
        '$ionicPopup',
        '$rootScope',
        'PubNubService',
        'md5',
        '$ionicScrollDelegate',
        '$timeout',

        function ($scope, $stateParams, $ionicPopup, $rootScope, PubNubService, md5, $ionicScrollDelegate, $timeout) {

            //window.onload = function () {

            // $(document).ready(function () {
            // Initialize the PubNub API connection.
            var message1 = "";
            var chatRoom = 'chat';
            var user;
            var foundChat = false;
            var pubnub = PubNubService;
            var historyCount = 0;

            $scope.$on("$ionicView.afterEnter", function (event) {
                console.log("i should be scrolling down");
                $timeout(function () {
                    $ionicScrollDelegate.scrollBottom();
                });
                //historyCount = 0;

            });

            if ($rootScope.userID) {
                console.log("confirming userid as:" + $rootScope.userID);

                var x = $rootScope.userID;
                var y = Parse.User.current().id;
                console.log("the fuckuser id is" + x);

                if (x > y) {
                    chatRoom = x + "" + y;

                    // we updload history using the chatroom we just created
                    getHistory();
                    var query = new Parse.Query('ChatRooms');
                    //query.include(' parent');
                    query.find({
                        success: function (results) {
                            // Do something with the returned Parse.Object values
                            foundChat = false;
                            for (var i = 0; i < results.length; i++) {
                                var object = results[i];
                                if (chatRoom == object.get('chat_name')) {
                                    console.log("chat room exists");
                                    foundChat = true;
                                    // if chat room exists, we check history count of messages in parse with pubnub
                                    updateHistoryCount();

                                }

                            }
                            if (!foundChat) {
                                console.log("chat room  does not exist");
                                createChatRoom();

                            }

                        }
                    });

                }
                else {
                    chatRoom = y + "" + x;

                    getHistory();
                    var query = new Parse.Query('ChatRooms');
                    //query.include(' parent');
                    query.find({
                        success: function (results) {
                            // Do something with the returned Parse.Object values
                            foundChat = false;
                            for (var i = 0; i < results.length; i++) {
                                var object = results[i];
                                if (chatRoom == object.get('chat_name')) {
                                    console.log("chat room exists");
                                    foundChat = true;
                                    updateHistoryCount();

                                }

                            }
                            if (!foundChat) {
                                createChatRoom();

                            }

                        }
                    });
                }

            }
            else {
                console.log("no user found");

            }

            // console.log(pubnub.uuid());
            var messageReceived = {};
            var existingListener;

            //var md =  md5.createHash('hamid' || '');

            if (!$rootScope.start) {
                // Initialize the PubNub service
                console.log("pubnub initialized");
                //pubnub = PubNub.getPub();

                $rootScope.start = true;
            }

            function getUser() {

            }

            function createChatRoom() {
                // chat room does not exist so we set up new chat room in DB

                var query = new Parse.Query('User');
                //query.include(' parent');
                console.log("getting the user");
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            console.log("looping throug users");
                            if ($rootScope.userID == object.id) {
                                var Chat = Parse.Object.extend("ChatRooms");
                                var newChat = new Chat();
                                newChat.set("chat_from", Parse.User.current().id);
                                newChat.save();
                                newChat.set("chat_to", object.id);
                                newChat.save();
                                newChat.set("chat_from_name", Parse.User.current().get("firstName"));
                                newChat.save();
                                console.log(object.get("username"));
                                newChat.set("chat_to_name", object.get("firstName"));
                                newChat.save();
                                newChat.set("chat_name", chatRoom);
                                newChat.set("HistoryCountMe", 0);
                                newChat.save();
                                newChat.set("HistoryCountTo", 0);
                                newChat.save();

                                console.log("chat room added to DB succesfully");

                            }
                        }

                    }
                });

            }
            function chatRoomExists() {

                var query = new Parse.Query('ChatRooms');
                //query.include(' parent');
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        foundChat = false;
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            if (chatRoom == object.get('chat_name')) {
                                console.log("chat room exists");
                                foundChat = true;

                            }

                        }
                        if (!foundChat) {
                            console.log("chat room  does not exist");

                        }

                    }
                });
            }

            function getHistory() {
                console.log("getting history");
                //console.log("the current historycount is:" + historyCount);
                pubnub.history({
                    channel: chatRoom,
                    count: 1000000,
                    callback: function (messages) {
                        messages[0].forEach(function (m) {
                            console.log("messages as: " + m.text);
                            historyCount++;
                            $scope.messages.push(m);
                        });
                        console.log("history counttTTTtt is: " + historyCount);

                    }
                });

            }
            // Subscribe to messages coming in from the channel.

            pubnub.subscribe({
                channel: chatRoom,
                withPresence: true,
                //connect: publish(),
                callback: function (m) {
                    handleMessage(m);

                },
            });

            function publish() {

                pubnub.publish({
                    channel: chatRoom,
                    message: $scope.data,
                });
                pubnub.publish({
                    channel: 'Global',
                    to: $scope.data.to,
                    from: $scope.data.from,
                    message: $scope.data,

                });

            };

            function updateHistoryCount() {

                var query = new Parse.Query('ChatRooms');
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        foundChat = false;
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            if (chatRoom == object.get('chat_name')) {
                                var historicalCount;
                                if (object.get('chat_from') == Parse.User.current().id) {
                                    if (object.get("HistoryCountTo") > historyCount) {
                                        historyCount = object.get("HistoryCountTo");
                                         object.set("HistoryCountMe", object.get("HistoryCountTo"));
                                        object.save();
                                        $rootScope.totalMessages = $rootScope.totalMessages  - (object.get("HistoryCountTo")-object.get("HistoryCountMe"));
                                    }
                                    else {
                                        object.set("HistoryCountMe", historyCount);
                                        object.save();
                                    }

                                }
                                else {
                                    if (object.get("HistoryCountMe") > historyCount) {
                                        historyCount = object.get("HistoryCountMe");
                                         object.set("HistoryCountTo", object.get("HistoryCountMe"));
                                        object.save();
                                         $rootScope.totalMessages = $rootScope.totalMessages  - (object.get("HistoryCountMe")-object.get("HistoryCountTo"));
                                    }
                                    else {
                                        object.set("HistoryCountTo", historyCount);
                                        object.save();
                                    }

                                }

                                if (historyCount != historicalCount) {
                                    console.log("we have a new motherfucking message, histories dont match");
                                    //newMessagesReceived = true;
                                }
                            }
                        }
                    }
                });

            }

            // Grab references for all of our elements.
            var messageContent = document.getElementById('messageContent'),
                // sendMessageButton = $('#sendMessageButton'),
                messageList = [];
            $scope.data = {};
            $scope.messages = [];

            // Handles all the messages coming in from pubnub.subscribe.

            function handleMessage(message) {
                /** var messageEl = $("
"
                     + "" + message.username + ": "
                     + message.text
                     + "
"); */
                console.log("we recevied message from: " + message.username);

                $scope.data = message;

                $scope.messages.push(message);
                var query = new Parse.Query('ChatRooms');
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            if (chatRoom == object.get('chat_name')) {
                                ++historyCount;
                                console.log(" i am HERE NOW and history count for" + object.id + "is:" + historyCount);
                                if (object.get('chat_from') == Parse.User.current().id) {
                                    object.set("HistoryCountMe", historyCount);
                                    object.save();

                                }
                                else {
                                    object.set("HistoryCountTo", historyCount);
                                    object.save();

                                }

                            }
                        }
                    }
                });

                $scope.$apply();
                console.log($scope.messages[$scope.messages.length - 1]);
                $timeout(function () {
                    $ionicScrollDelegate.scrollBottom(true);
                }, 300);

                //$scope.data = null;

                // Scroll to bottom of page
                //  $("html, body").animate({ scrollTop: $(document).height() - $(window).height() }, 'slow');
            };

            // Compose and send a message when the user clicks our send message button.
            //while (1==1) {}

            $scope.sendMessage = function (event) {
                message1 = messageContent.value;

                if (message1 != '') {
                    var messagetobeSent = {
                        username: Parse.User.current().get('firstName'),
                        text: message1,
                        to: $rootScope.userID,
                        from: Parse.User.current().id
                    }
                    $scope.data = messagetobeSent;
                    //  $scope.messages.push($scope.data);
                    console.log(Parse.User.current().get('firstName'));
                    publish();
                    // Parse.Cloud.useMasterKey();
                    var query = new Parse.Query('User');
                    //query.include(' parent');
                    query.equalTo("objectId", $rootScope.userID);
                    console.log("getting the user");
                    Parse.Cloud.run('incrementChatCount', { objectId: $rootScope.userID }, {
                        success: function (results) {
                            console.log("success");

                        },
                        error: function (error) {
                            console.error(error);
                        }
                    });

                    messageContent.value = "Message";

                }
            };

            $scope.getBubbleClass = function (username) {
                var classname = 'from-them';
                console.log("formatting chat");
                if ($scope.messageIsMine(username)) {
                    classname = 'from-me';
                }
                return classname;
            };

            $scope.messageIsMine = function (username) {
                return username === Parse.User.current().get('firstName');
            };
            // Also send a message when the user hits the enter button in the text area.
            /* messageContent.bind('keydown', function (event) {
                  if ((event.keyCode || event.charCode) !== 13) return true;
                  sendMessageButton.click();
                  return false;
              });

*/

            // });

        }
        //}
    ]);
});<commit_msg>updating chat again and againndfnnw<commit_after>define([
    'app',
    'services/event',

], function (app) {
    'use strict';

    app.controller('ChatCtrl', [
        '$scope',
        '$state',
        '$ionicPopup',
        '$rootScope',
        'PubNubService',
        'md5',
        '$ionicScrollDelegate',
        '$timeout',

        function ($scope, $stateParams, $ionicPopup, $rootScope, PubNubService, md5, $ionicScrollDelegate, $timeout) {

            //window.onload = function () {

            // $(document).ready(function () {
            // Initialize the PubNub API connection.
            var message1 = "";
            var chatRoom = 'chat';
            var user;
            var foundChat = false;
            var pubnub = PubNubService;
            var historyCount = 0;

            $scope.$on("$ionicView.afterEnter", function (event) {
                console.log("i should be scrolling down");
                $timeout(function () {
                    $ionicScrollDelegate.scrollBottom();
                });
                //historyCount = 0;

            });

            if ($rootScope.userID) {
                console.log("confirming userid as:" + $rootScope.userID);

                var x = $rootScope.userID;
                var y = Parse.User.current().id;
                console.log("the fuckuser id is" + x);

                if (x > y) {
                    chatRoom = x + "" + y;

                    // we updload history using the chatroom we just created
                    getHistory();
                    var query = new Parse.Query('ChatRooms');
                    //query.include(' parent');
                    query.find({
                        success: function (results) {
                            // Do something with the returned Parse.Object values
                            foundChat = false;
                            for (var i = 0; i < results.length; i++) {
                                var object = results[i];
                                if (chatRoom == object.get('chat_name')) {
                                    console.log("chat room exists");
                                    foundChat = true;
                                    // if chat room exists, we check history count of messages in parse with pubnub
                                    updateHistoryCount();

                                }

                            }
                            if (!foundChat) {
                                console.log("chat room  does not exist");
                                createChatRoom();

                            }

                        }
                    });

                }
                else {
                    chatRoom = y + "" + x;

                    getHistory();
                    var query = new Parse.Query('ChatRooms');
                    //query.include(' parent');
                    query.find({
                        success: function (results) {
                            // Do something with the returned Parse.Object values
                            foundChat = false;
                            for (var i = 0; i < results.length; i++) {
                                var object = results[i];
                                if (chatRoom == object.get('chat_name')) {
                                    console.log("chat room exists");
                                    foundChat = true;
                                    updateHistoryCount();

                                }

                            }
                            if (!foundChat) {
                                createChatRoom();

                            }

                        }
                    });
                }

            }
            else {
                console.log("no user found");

            }

            // console.log(pubnub.uuid());
            var messageReceived = {};
            var existingListener;

            //var md =  md5.createHash('hamid' || '');

            if (!$rootScope.start) {
                // Initialize the PubNub service
                console.log("pubnub initialized");
                //pubnub = PubNub.getPub();

                $rootScope.start = true;
            }

            function getUser() {

            }

            function createChatRoom() {
                // chat room does not exist so we set up new chat room in DB

                var query = new Parse.Query('User');
                //query.include(' parent');
                console.log("getting the user");
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            console.log("looping throug users");
                            if ($rootScope.userID == object.id) {
                                var Chat = Parse.Object.extend("ChatRooms");
                                var newChat = new Chat();
                                newChat.set("chat_from", Parse.User.current().id);
                                newChat.save();
                                newChat.set("chat_to", object.id);
                                newChat.save();
                                newChat.set("chat_from_name", Parse.User.current().get("firstName"));
                                newChat.save();
                                console.log(object.get("username"));
                                newChat.set("chat_to_name", object.get("firstName"));
                                newChat.save();
                                newChat.set("chat_name", chatRoom);
                                newChat.set("HistoryCountMe", 0);
                                newChat.save();
                                newChat.set("HistoryCountTo", 0);
                                newChat.save();

                                console.log("chat room added to DB succesfully");

                            }
                        }

                    }
                });

            }
            function chatRoomExists() {

                var query = new Parse.Query('ChatRooms');
                //query.include(' parent');
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        foundChat = false;
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            if (chatRoom == object.get('chat_name')) {
                                console.log("chat room exists");
                                foundChat = true;

                            }

                        }
                        if (!foundChat) {
                            console.log("chat room  does not exist");

                        }

                    }
                });
            }

            function getHistory() {
                console.log("getting history");
                //console.log("the current historycount is:" + historyCount);
                pubnub.history({
                    channel: chatRoom,
                    count: 1000000,
                    callback: function (messages) {
                        messages[0].forEach(function (m) {
                            console.log("messages as: " + m.text);
                            historyCount++;
                            $scope.messages.push(m);
                        });
                        console.log("history counttTTTtt is: " + historyCount);

                    }
                });

            }
            // Subscribe to messages coming in from the channel.

            pubnub.subscribe({
                channel: chatRoom,
                withPresence: true,
                //connect: publish(),
                callback: function (m) {
                    handleMessage(m);

                },
            });

            function publish() {

                pubnub.publish({
                    channel: chatRoom,
                    message: $scope.data,
                });
                pubnub.publish({
                    channel: 'Global',
                    to: $scope.data.to,
                    from: $scope.data.from,
                    message: $scope.data,

                });

            };

            function updateHistoryCount() {

                var query = new Parse.Query('ChatRooms');
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        foundChat = false;
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            if (chatRoom == object.get('chat_name')) {
                                var historicalCount;
                                if (object.get('chat_from') == Parse.User.current().id) {
                                    if (object.get("HistoryCountTo") > historyCount) {
                                        historyCount = object.get("HistoryCountTo");
                                        // object.set("HistoryCountMe", object.get("HistoryCountTo"));
                                        object.save();
                                        $rootScope.totalMessages = $rootScope.totalMessages  - (object.get("HistoryCountTo")-object.get("HistoryCountMe"));
                                    }
                                    else {
                                        object.set("HistoryCountMe", historyCount);
                                        object.save();
                                    }

                                }
                                else {
                                    if (object.get("HistoryCountMe") > historyCount) {
                                        historyCount = object.get("HistoryCountMe");
                                       //  object.set("HistoryCountTo", object.get("HistoryCountMe"));
                                        object.save();
                                         $rootScope.totalMessages = $rootScope.totalMessages  - (object.get("HistoryCountMe")-object.get("HistoryCountTo"));
                                    }
                                    else {
                                        object.set("HistoryCountTo", historyCount);
                                        object.save();
                                    }

                                }

                                if (historyCount != historicalCount) {
                                    console.log("we have a new motherfucking message, histories dont match");
                                    //newMessagesReceived = true;
                                }
                            }
                        }
                    }
                });

            }

            // Grab references for all of our elements.
            var messageContent = document.getElementById('messageContent'),
                // sendMessageButton = $('#sendMessageButton'),
                messageList = [];
            $scope.data = {};
            $scope.messages = [];

            // Handles all the messages coming in from pubnub.subscribe.

            function handleMessage(message) {
                /** var messageEl = $("
"
                     + "" + message.username + ": "
                     + message.text
                     + "
"); */
                console.log("we recevied message from: " + message.username);

                $scope.data = message;

                $scope.messages.push(message);
                var query = new Parse.Query('ChatRooms');
                query.find({
                    success: function (results) {
                        // Do something with the returned Parse.Object values
                        for (var i = 0; i < results.length; i++) {
                            var object = results[i];
                            if (chatRoom == object.get('chat_name')) {
                                ++historyCount;
                                console.log(" i am HERE NOW and history count for" + object.id + "is:" + historyCount);
                                if (object.get('chat_from') == Parse.User.current().id) {
                                    object.set("HistoryCountMe", historyCount);
                                    object.save();

                                }
                                else {
                                    object.set("HistoryCountTo", historyCount);
                                    object.save();

                                }

                            }
                        }
                    }
                });

                $scope.$apply();
                console.log($scope.messages[$scope.messages.length - 1]);
                $timeout(function () {
                    $ionicScrollDelegate.scrollBottom(true);
                }, 300);

                //$scope.data = null;

                // Scroll to bottom of page
                //  $("html, body").animate({ scrollTop: $(document).height() - $(window).height() }, 'slow');
            };

            // Compose and send a message when the user clicks our send message button.
            //while (1==1) {}

            $scope.sendMessage = function (event) {
                message1 = messageContent.value;

                if (message1 != '') {
                    var messagetobeSent = {
                        username: Parse.User.current().get('firstName'),
                        text: message1,
                        to: $rootScope.userID,
                        from: Parse.User.current().id
                    }
                    $scope.data = messagetobeSent;
                    //  $scope.messages.push($scope.data);
                    console.log(Parse.User.current().get('firstName'));
                    publish();
                    // Parse.Cloud.useMasterKey();
                    var query = new Parse.Query('User');
                    //query.include(' parent');
                    query.equalTo("objectId", $rootScope.userID);
                    console.log("getting the user");
                    Parse.Cloud.run('incrementChatCount', { objectId: $rootScope.userID }, {
                        success: function (results) {
                            console.log("success");

                        },
                        error: function (error) {
                            console.error(error);
                        }
                    });

                    messageContent.value = "Message";

                }
            };

            $scope.getBubbleClass = function (username) {
                var classname = 'from-them';
                console.log("formatting chat");
                if ($scope.messageIsMine(username)) {
                    classname = 'from-me';
                }
                return classname;
            };

            $scope.messageIsMine = function (username) {
                return username === Parse.User.current().get('firstName');
            };
            // Also send a message when the user hits the enter button in the text area.
            /* messageContent.bind('keydown', function (event) {
                  if ((event.keyCode || event.charCode) !== 13) return true;
                  sendMessageButton.click();
                  return false;
              });

*/

            // });

        }
        //}
    ]);
});<|endoftext|>


<commit_before>console.log('Portal Large Display');

/* Acme Namespace */
var acme = acme || {};

/**
 * @constructor
 */
acme.Util = function() {
};

/**
 * @param {string} filePath The location of the file to inject.
 */
acme.Util.injectScript = function(filePath) {
  // Inject content script.
  var s = document.createElement('script');
  s.src = chrome.extension.getURL(filePath);
  (document.head || document.documentElement).appendChild(s);
  s.onload = function() {
      s.parentNode.removeChild(s);
  };
};

acme.Util.injectScript('contentjs/inject.js');

/*
 * Load the style overrides.
 */
var portalStyleOverrides = document.createElement('link');
portalStyleOverrides.setAttribute('rel', 'stylesheet');
portalStyleOverrides.setAttribute('type', 'text/css');
portalStyleOverrides.setAttribute('href',
    chrome.extension.getURL('css/acme_display.css'));
document.getElementsByTagName('head')[0].appendChild(portalStyleOverrides);

/**
 * @constructor
 * @param {number} lat Latitude in degrees N, (-90, 90).
 * @param {number} lon Longitude in degrees E, [-180, 180).
 * @param {number} alt Altitude (meters).
 * @param {number} heading Heading in degrees, [0-360).
 * @param {number} tilt Tilt in degrees, [0 == down, 90 == horizon).
 * @param {number} roll Roll (unused).
 */
Pose = function(lat, lon, alt, heading, tilt, roll) {
  this.lat = lat || 0.0;
  this.lon = lon || 0.0;
  this.alt = alt || 0.0;
  this.heading = heading || 0.0;
  this.tilt = tilt || 0.0;
  this.roll = roll || 0.0;
};

/**
 * Planet values.
 */
Planet = {
  EARTH: 1,
  MOON: 2,
  MARS: 3
};

/**
 * Send a custom event to the page.
 *
 * @param {Object.<{context: string, method: string, args: Array}>} request
 */
acme.Util.sendCustomEvent = function(request) {
    var eventRequest = new CustomEvent('acme-event', {
      'detail': request,
      'canBubble': false,
      'cancelable': false
    });
    document.dispatchEvent(eventRequest);
};

/**
 * Responsible for large display business logic.
 * @constructor
 */
acme.Display = function() {
  /** @type [boolean] */
  this.hasInitialized = false;
};

/**
 * Does all of the needed initial setup.  Also
 * ensures it is only run once.
 */
acme.Display.prototype.initOnce = function() {
  if (this.hasInitialized) return;

  console.log('Display initialized.');
  this.setLargeDisplayMode();

  this.hasInitialized = true;
};

/**
 * Calls into the page to indicate it should set itself up as a
 * "large display."
 */
acme.Display.prototype.setLargeDisplayMode = function() {
  acme.Util.sendCustomEvent({
    method: 'setLargeDisplayMode'
  });
};

/** The ACME Display object. */
acme.display = new acme.Display();

/**
 * Keep this in sync with core:tactile.acme.InputSupport_
* @enum {number}
* @private
*/
var InputSupport_ = {
  NONE: 0,
  DISABLED: 1,
  NO_ZOOM: 2,
  NO_ZOOM_NO_PAN: 3
};

var dumpUpdateToScreen = function(message) {
  var stringifiedMessage = JSON.stringify(message);
  var debugArea = document.getElementById('portaldebug');
  if (!debugArea) {
    debugArea = document.createElement('div');
    debugArea.id = 'portaldebug';
    debugArea.style.position = 'fixed';
    debugArea.style.bottom = '90px';
    debugArea.style.left = '0px';
    debugArea.style.height = '30px';
    debugArea.style.width = '100%';
    debugArea.style.zIndex = '99999';
    debugArea.style.backgroundColor = 'rgba(242, 242, 242, 0.7)';
    document.body.appendChild(debugArea);
  }
  debugArea.textContent = stringifiedMessage;
};

var publishDisplayCurrentPose = function(pose) {
  var portalPose = new ROSLIB.Message({
    current_pose: {
      position: {
        x: pose.lon,
        y: pose.lat,
        z: pose.alt
      },
      orientation: {
        x: pose.tilt,
        y: pose.roll,
        z: pose.heading,
        w: 0
      }
    }
  });

  if (handOverlay) {
    handOverlay.setCurrentCameraPose(pose);
  }
  portalDisplayCurrentPoseTopic.publish(portalPose);
};

var cameraUpdateHandler = function(cameraEvent) {
  // Lazy set the large display mode after we get our first
  // stable camera update.
  acme.display.initOnce();

  publishDisplayCurrentPose(cameraEvent.detail);
};

/**
 * Move Camera.
 * @param {Pose} pose The Camera Pose.
 * @param {boolean} shouldAnimate True/false to choose animate/warp to pose.
 */
acme.Display.prototype.moveCamera = function(pose, shouldAnimate) {
  acme.Util.sendCustomEvent({
      method: 'moveCamera',
      args: [pose, shouldAnimate]
  });
};

var getPointInfo = function(screenX, screenY) {
  acme.Util.sendCustomEvent({
      method: 'toLocationEvent',
      args: [screenX, screenY]
  });
};

var startsWith = function startsWith(s, str) {
  return s.slice(0, str.length) == str;
};

/* Add tactile event listeners. */
window.addEventListener('acmeCameraUpdate', cameraUpdateHandler, true);
window.addEventListener('acmeStableCameraUpdate', cameraUpdateHandler, true);
window.addEventListener('acmeCameraCallback', cameraUpdateHandler, true);

/** Add ROS Subscriptions.  These are almost last to prevent exception from
 * killing the event listeners when ROS is down. */
var portalRosDisplay = new ROSLIB.Ros({
  url: 'wss://42-b:9090'
});

// Globe View Topic listens and publishes camera updates.
var navigatorListener = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/portal_nav/display_goto_pose',
  messageType: 'geometry_msgs/PoseStamped',
  throttle_rate: 30,
  queue_length: 2
});

var portalDisplayCurrentPoseTopic = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/portal_display/current_pose',
  messageType: 'portal_nav/PortalPose'
});

// Controls whether or not we listen to moveto events.  When on a tour
// we should not listen to moveto events.
var ignoreCameraUpdates = false;

navigatorListener.subscribe(function(rosPoseStamped) {
  if (!ignoreCameraUpdates) {
    var pose = new Pose(rosPoseStamped.pose.position.y,  // lat
                        rosPoseStamped.pose.position.x,  // lon
                        rosPoseStamped.pose.position.z,  // alt
                        rosPoseStamped.pose.orientation.z,  // heading
                        rosPoseStamped.pose.orientation.x,  // tilt
                        rosPoseStamped.pose.orientation.y);  // roll
    acme.display.moveCamera(pose, false);
  }
});

var runwayContentTopic = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/portal_kiosk/runway',
  // TODO(daden): quick hack to get the string across, we should create our own
  // ROS message for passing this data across in the future.
  messageType: 'std_msgs/String'
});

var runwayContentSubscriber = function(message) {
  console.log(message);
  var runwayContentEvents = {
    CLICK: 'click!!',
    EXIT: 'exit!!'
  };
  var data = message.data;
  if (startsWith(data, runwayContentEvents.CLICK)) {
    var customDataStr = data.slice(
        runwayContentEvents.CLICK.length, data.length);
    var customData = JSON.parse(customDataStr);
    var sceneContentArray = customData[1];
    var runwayImageType = customData[2];

    var planetChange = sceneContentArray[7];

    // Check to see if this is the type of runway element that should
    // not use the pose information coming from anywhere.
    if (!planetChange && runwayImageType == InputSupport_.DISABLED) {
      ignoreCameraUpdates = true;
    } else {
      ignoreCameraUpdates = false;
    }

    // disable HUD unless changing planet to Earth
    if (planetChange && planetChange == Planet.EARTH) {
      handOverlay.enabled = true;
    } else {
      handOverlay.enabled = false;
    }

    // disable spacenav feedback unless changing planets
    if (runwayImageType != InputSupport_.NONE && !planetChange) {
      spacenavFeedback.enabled = false;
    }

    acme.Util.sendCustomEvent({
        method: 'launchRunwayContent',
        args: [sceneContentArray]
    });
  } else if (startsWith(data, runwayContentEvents.EXIT)) {
    // we assume there is no runway content on Moon or Mars
    handOverlay.enabled = true;
    spacenavFeedback.enabled = true;
    ignoreCameraUpdates = false;
    acme.Util.sendCustomEvent({
        method: 'exitTitleCard'
    });
  } else {
    console.error('Unhandled runway content data: ' + data);
  }
};
runwayContentTopic.subscribe(runwayContentSubscriber);

var populationService = new ROSLIB.Service({
  ros: portalRosDisplay,
  name: '/geodata/population',
  serviceType: 'geodata/GeodataQuery'
});

window.addEventListener('acmePopulationQuery', function(ev) {
  var populationRequest = new ROSLIB.ServiceRequest({
    layer: 'population',
    point: {
      latitude: ev.detail.latitude,
      longitude: ev.detail.longitude,
      altitude: 0
    },
    radius: ev.detail.radius
  });

  populationService.callService(
    populationRequest,
    ev.detail.callback,
    function(err) {
      console.error('querying population service:', err);
    }
  );
}, true);

/**
 * A common WebGL environment for visual modules.
 */
acme.glEnvironment = new PortalGLEnvironment();

var leapListener = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/leap_motion/frame',
  messageType: 'leap_motion/Frame',
  throttle_rate: 30
});

// TODO(daden): detect if webGL is available before trying
// to load the hand.  If webGL isn't available this code crashes.
// Init the hand last so if it fails to load it doesn't crash.
var handOverlay = new HandOverlay(acme.glEnvironment);
handOverlay.init3js();
leapListener.subscribe(handOverlay.processLeapMessage);
window.addEventListener('acmeScreenLocation',
    handOverlay.processHandGeoLocationEvent.bind(handOverlay), true);

var spacenavListener = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/spacenav/twist',
  messageType: 'geometry_msgs/Twist',
  throttle_rate: 30
});

var spacenavFeedback = new SpacenavFeedback(acme.glEnvironment);
spacenavFeedback.init();
spacenavListener.subscribe(
  spacenavFeedback.processSpacenavMessage.bind(spacenavFeedback)
);

<commit_msg>Removed a few redundant events.<commit_after>console.log('Portal Large Display');

/* Acme Namespace */
var acme = acme || {};

/**
 * @constructor
 */
acme.Util = function() {
};

/**
 * @param {string} filePath The location of the file to inject.
 */
acme.Util.injectScript = function(filePath) {
  // Inject content script.
  var s = document.createElement('script');
  s.src = chrome.extension.getURL(filePath);
  (document.head || document.documentElement).appendChild(s);
  s.onload = function() {
      s.parentNode.removeChild(s);
  };
};

acme.Util.injectScript('contentjs/inject.js');

/*
 * Load the style overrides.
 */
var portalStyleOverrides = document.createElement('link');
portalStyleOverrides.setAttribute('rel', 'stylesheet');
portalStyleOverrides.setAttribute('type', 'text/css');
portalStyleOverrides.setAttribute('href',
    chrome.extension.getURL('css/acme_display.css'));
document.getElementsByTagName('head')[0].appendChild(portalStyleOverrides);

/**
 * @constructor
 * @param {number} lat Latitude in degrees N, (-90, 90).
 * @param {number} lon Longitude in degrees E, [-180, 180).
 * @param {number} alt Altitude (meters).
 * @param {number} heading Heading in degrees, [0-360).
 * @param {number} tilt Tilt in degrees, [0 == down, 90 == horizon).
 * @param {number} roll Roll (unused).
 */
Pose = function(lat, lon, alt, heading, tilt, roll) {
  this.lat = lat || 0.0;
  this.lon = lon || 0.0;
  this.alt = alt || 0.0;
  this.heading = heading || 0.0;
  this.tilt = tilt || 0.0;
  this.roll = roll || 0.0;
};

/**
 * Planet values.
 */
Planet = {
  EARTH: 1,
  MOON: 2,
  MARS: 3
};

/**
 * Send a custom event to the page.
 *
 * @param {Object.<{context: string, method: string, args: Array}>} request
 */
acme.Util.sendCustomEvent = function(request) {
    var eventRequest = new CustomEvent('acme-event', {
      'detail': request,
      'canBubble': false,
      'cancelable': false
    });
    document.dispatchEvent(eventRequest);
};

/**
 * Responsible for large display business logic.
 * @constructor
 */
acme.Display = function() {
  /** @type [boolean] */
  this.hasInitialized = false;
};

/**
 * Does all of the needed initial setup.  Also
 * ensures it is only run once.
 */
acme.Display.prototype.initOnce = function() {
  if (this.hasInitialized) return;

  console.log('Display initialized.');
  this.setLargeDisplayMode();

  this.hasInitialized = true;
};

/**
 * Calls into the page to indicate it should set itself up as a
 * "large display."
 */
acme.Display.prototype.setLargeDisplayMode = function() {
  acme.Util.sendCustomEvent({
    method: 'setLargeDisplayMode'
  });
};

/** The ACME Display object. */
acme.display = new acme.Display();

/**
 * Keep this in sync with core:tactile.acme.InputSupport_
* @enum {number}
* @private
*/
var InputSupport_ = {
  NONE: 0,
  DISABLED: 1,
  NO_ZOOM: 2,
  NO_ZOOM_NO_PAN: 3
};

var dumpUpdateToScreen = function(message) {
  var stringifiedMessage = JSON.stringify(message);
  var debugArea = document.getElementById('portaldebug');
  if (!debugArea) {
    debugArea = document.createElement('div');
    debugArea.id = 'portaldebug';
    debugArea.style.position = 'fixed';
    debugArea.style.bottom = '90px';
    debugArea.style.left = '0px';
    debugArea.style.height = '30px';
    debugArea.style.width = '100%';
    debugArea.style.zIndex = '99999';
    debugArea.style.backgroundColor = 'rgba(242, 242, 242, 0.7)';
    document.body.appendChild(debugArea);
  }
  debugArea.textContent = stringifiedMessage;
};

var publishDisplayCurrentPose = function(pose) {
  var portalPose = new ROSLIB.Message({
    current_pose: {
      position: {
        x: pose.lon,
        y: pose.lat,
        z: pose.alt
      },
      orientation: {
        x: pose.tilt,
        y: pose.roll,
        z: pose.heading,
        w: 0
      }
    }
  });

  if (handOverlay) {
    handOverlay.setCurrentCameraPose(pose);
  }
  portalDisplayCurrentPoseTopic.publish(portalPose);
};

var cameraUpdateHandler = function(cameraEvent) {
  // Lazy set the large display mode after we get our first
  // stable camera update.
  acme.display.initOnce();

  publishDisplayCurrentPose(cameraEvent.detail);
};

/**
 * Move Camera.
 * @param {Pose} pose The Camera Pose.
 * @param {boolean} shouldAnimate True/false to choose animate/warp to pose.
 */
acme.Display.prototype.moveCamera = function(pose, shouldAnimate) {
  acme.Util.sendCustomEvent({
      method: 'moveCamera',
      args: [pose, shouldAnimate]
  });
};

var getPointInfo = function(screenX, screenY) {
  acme.Util.sendCustomEvent({
      method: 'toLocationEvent',
      args: [screenX, screenY]
  });
};

var startsWith = function startsWith(s, str) {
  return s.slice(0, str.length) == str;
};

/* Add tactile event listeners. */
window.addEventListener('acmeCameraUpdate', cameraUpdateHandler, true);
window.addEventListener('acmeStableCameraUpdate', cameraUpdateHandler, true);
window.addEventListener('acmeCameraCallback', cameraUpdateHandler, true);

/** Add ROS Subscriptions.  These are almost last to prevent exception from
 * killing the event listeners when ROS is down. */
var portalRosDisplay = new ROSLIB.Ros({
  url: 'wss://42-b:9090'
});

// Globe View Topic listens and publishes camera updates.
var navigatorListener = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/portal_nav/display_goto_pose',
  messageType: 'geometry_msgs/PoseStamped',
  throttle_rate: 30,
  queue_length: 2
});

var portalDisplayCurrentPoseTopic = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/portal_display/current_pose',
  messageType: 'portal_nav/PortalPose'
});

navigatorListener.subscribe(function(rosPoseStamped) {
  var pose = new Pose(rosPoseStamped.pose.position.y,  // lat
                      rosPoseStamped.pose.position.x,  // lon
                      rosPoseStamped.pose.position.z,  // alt
                      rosPoseStamped.pose.orientation.z,  // heading
                      rosPoseStamped.pose.orientation.x,  // tilt
                      rosPoseStamped.pose.orientation.y);  // roll
  acme.display.moveCamera(pose, false);
});

var runwayContentTopic = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/portal_kiosk/runway',
  // TODO(daden): quick hack to get the string across, we should create our own
  // ROS message for passing this data across in the future.
  messageType: 'std_msgs/String'
});

var runwayContentSubscriber = function(message) {
  var runwayContentEvents = {
    CLICK: 'click!!',
    EXIT: 'exit!!'
  };
  var data = message.data;
  if (startsWith(data, runwayContentEvents.CLICK)) {
    var customDataStr = data.slice(
        runwayContentEvents.CLICK.length, data.length);
    var customData = JSON.parse(customDataStr);
    var sceneContentArray = customData[1];
    var runwayImageType = customData[2];

    var planetChange = sceneContentArray[7];

    /*
    // Check to see if this is the type of runway element that should
    // not use the pose information coming from anywhere.
    if (!planetChange && runwayImageType == InputSupport_.DISABLED) {
      console.log("Ignoring updates.");
    } else {
      console.log("Listening to updates.");
    }
    */

    // disable HUD unless changing planet to Earth
    if (planetChange && planetChange == Planet.EARTH) {
      handOverlay.enabled = true;
    } else {
      handOverlay.enabled = false;
    }

    // disable spacenav feedback unless changing planets
    if (runwayImageType != InputSupport_.NONE && !planetChange) {
      spacenavFeedback.enabled = false;
    }

    acme.Util.sendCustomEvent({
        method: 'launchRunwayContent',
        args: [sceneContentArray]
    });
  } else if (startsWith(data, runwayContentEvents.EXIT)) {
    // we assume there is no runway content on Moon or Mars
    handOverlay.enabled = true;
    spacenavFeedback.enabled = true;
    console.log("Listening to updates from EXIT.");
    acme.Util.sendCustomEvent({
        method: 'exitTitleCard'
    });
  } else {
    console.error('Unhandled runway content data: ' + data);
  }
};
runwayContentTopic.subscribe(runwayContentSubscriber);

var populationService = new ROSLIB.Service({
  ros: portalRosDisplay,
  name: '/geodata/population',
  serviceType: 'geodata/GeodataQuery'
});

window.addEventListener('acmePopulationQuery', function(ev) {
  var populationRequest = new ROSLIB.ServiceRequest({
    layer: 'population',
    point: {
      latitude: ev.detail.latitude,
      longitude: ev.detail.longitude,
      altitude: 0
    },
    radius: ev.detail.radius
  });

  populationService.callService(
    populationRequest,
    ev.detail.callback,
    function(err) {
      console.error('querying population service:', err);
    }
  );
}, true);

/**
 * A common WebGL environment for visual modules.
 */
acme.glEnvironment = new PortalGLEnvironment();

var leapListener = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/leap_motion/frame',
  messageType: 'leap_motion/Frame',
  throttle_rate: 30
});

// TODO(daden): detect if webGL is available before trying
// to load the hand.  If webGL isn't available this code crashes.
// Init the hand last so if it fails to load it doesn't crash.
var handOverlay = new HandOverlay(acme.glEnvironment);
handOverlay.init3js();
leapListener.subscribe(handOverlay.processLeapMessage);
window.addEventListener('acmeScreenLocation',
    handOverlay.processHandGeoLocationEvent.bind(handOverlay), true);

var spacenavListener = new ROSLIB.Topic({
  ros: portalRosDisplay,
  name: '/spacenav/twist',
  messageType: 'geometry_msgs/Twist',
  throttle_rate: 30
});

var spacenavFeedback = new SpacenavFeedback(acme.glEnvironment);
spacenavFeedback.init();
spacenavListener.subscribe(
  spacenavFeedback.processSpacenavMessage.bind(spacenavFeedback)
);

<|endoftext|>


<commit_before>"use strict";

// Local tweet test override.
var canTweetFromLocal = false;

// Load libraries.
var _ = require('underscore');
var wordfilter = require('wordfilter');
var languageDetector = require('cld');
var SC = require('node-soundcloud');
var Twit = require('twit');

// Are we on production? Check if an important environment variable exists.
function isProduction () {
  return (typeof(process.env.TWITTER_CONSUMER_KEY) !== 'undefined');
}

// Use environment variables if we're on production, config files if we're local.
if (isProduction()) {
  SC.init({
    id:     process.env.SOUNDCLOUD_CLIENT_ID,
    secret: process.env.SOUNDCLOUD_SECRET
  });

  var twitter = new Twit({
    consumer_key: process.env.TWITTER_CONSUMER_KEY,
    consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
    access_token: process.env.TWITTER_ACCESS_TOKEN,
    access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
  });
}
else {
  SC.init(require('./soundcloud-config.js'));
  var twitter = new Twit(require('./twitter-config.js'));
}

// Execute once upon initialization.
makeAndPostTweet();

// Get a handful of comments and choose one.
function makeAndPostTweet () {
  getComment()
    .then(function (results) {
      postTweet(results);
    })
    .catch(function (err) {
      console.log('ERROR:', err);
      makeAndPostTweet();
    });
}

function getComment () {

  return new Promise (function (resolve, reject) {

    console.log('\n---\n');

    // Pick a random comment. The API doesn't provide for this, but SoundCloud
    // comment IDs are sequential! There are a lot of missing comments (deleted
    // spam, etc.), but this has about a 40-50% success rate at finding an
    // actual comment, which is pretty good!
    var randomCommentID = String(_.random(100000000, 300000000));

    // Query the SoundCloud API and filter the results.
    SC.get('/comments/' + randomCommentID, function(err, comment) {

      // console.log('\n\nFULL API RESPONSE:\n\n', comment)

      console.log('LOOKING FOR A COMMENT…');

      if (typeof(err) !== 'undefined') {
        reject('Comment does not exist.');
      }
      else {
        var comment = comment.body.trim();

        console.log('FOUND A COMMENT:', comment);

        console.log('ANALYZING: Checking if too short…');

        if (comment.length < 1) {
          reject('Comment is too short.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if too long…');

        if (comment.length > 141) {
          reject('Comment is too long');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking for bad words…');

        if (wordfilter.blacklisted(comment)) {
          reject('Comment is a reply, contains a bad word, or looks like spam.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if is English…');

        if (!isEnglish(comment)) {
          reject('Comment is not in English.');
          return;
        }

        console.log('\tOK!');

        console.log('SUCCESS: All checks passed! Comment is useable!');
        resolve(comment);
      }

    });
  });
}

function postTweet (tweet) {

  if (typeof(tweet) !== 'undefined') {
    console.log('NOW TWEETING:', tweet);

    if (isProduction() || canTweetFromLocal) {
      twitter.post('statuses/update', { status: tweet }, function (error) {
        if (error) {
          console.log('ERROR POSTING TWEET:', error);
        }
      });
    }
  }
  else {
    console.log('ERROR: No comment was fetched!');
  }

}

// Tweet on a regular schedule. 8 times a day means every 3 hours. Note that
// Because Heroku cycles dynos once per day, the bot's schedule will not be
// regular: https://devcenter.heroku.com/articles/how-heroku-works#dyno-manager
if (isProduction()) {
  var timesToTweetPerDay = 8;

  setInterval(function () {
    try {
      makeAndPostTweet();
    }
    catch (err) {
      console.log('PROCESS UNSUCCESSFUL!', err);
    }
  }, (1000 * 60 * 60 * 24) / timesToTweetPerDay);
}

///

// Is this English?
function isEnglish (comment) {

  var options = {
    isHTML       : false,
    encodingHint : 'UTF8UTF8',
    tldHint      : 'com'
  };

  return languageDetector.detect(comment, options, function(err, result) {

    if (!_.isEmpty(err)) {
      console.log('LANGUAGE DETECTION ERROR:', err.message);

      // If the language couldn't be detected, just show the tweet. Most of the
      // time this means it's slang or internet-speak and is fine to print.
      if (err.message === 'Failed to identify language') {
        console.log('LANGUAGE DETECTION OVERRIDE: Approving as English anyway, just for kicks.');

        return true;
      }
    }
    else {
      return (result.languages[0].name === 'ENGLISH');
    }

  });

}

// Additional filters.
wordfilter.addWords([

  // To get a better set of comments, I'm filtering out a lot of things.
  // The lists below are used in addition to the default set:
  // https://github.com/dariusk/wordfilter/blob/master/lib/badwords.json

  // Promotion.
  'follow',
  'listen to my',
  'check out',
  'check my',
  'subscribe',
  'channel',
  'my cloud',
  'add me',

  'soundcloud',
  'facebook',
  'twitter',
  'youtube',
  'instagram',
  'blog',

  'free',
  'download',

  // Fake comments.
  'repost',
  'posted',
  'full support',
  'fully support',

  // Traditional spam.
  'sex',
  'cam',
  'dollars',
  'money',

  // URLs.
  'http',
  'www',
  '.co',
  '.net',

  // Replies, SoundCloud links, tags, etc.
  'user-',
  '\@',
  '\n',
  '_',
  '#',

  // A few curses and bad words.
  'fuck',
  'rape'

]);
<commit_msg>Indicate the ID of the comment we randomly chose.<commit_after>"use strict";

// Local tweet test override.
var canTweetFromLocal = false;

// Load libraries.
var _ = require('underscore');
var wordfilter = require('wordfilter');
var languageDetector = require('cld');
var SC = require('node-soundcloud');
var Twit = require('twit');

// Are we on production? Check if an important environment variable exists.
function isProduction () {
  return (typeof(process.env.TWITTER_CONSUMER_KEY) !== 'undefined');
}

// Use environment variables if we're on production, config files if we're local.
if (isProduction()) {
  SC.init({
    id:     process.env.SOUNDCLOUD_CLIENT_ID,
    secret: process.env.SOUNDCLOUD_SECRET
  });

  var twitter = new Twit({
    consumer_key: process.env.TWITTER_CONSUMER_KEY,
    consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
    access_token: process.env.TWITTER_ACCESS_TOKEN,
    access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
  });
}
else {
  SC.init(require('./soundcloud-config.js'));
  var twitter = new Twit(require('./twitter-config.js'));
}

// Execute once upon initialization.
makeAndPostTweet();

// Get a handful of comments and choose one.
function makeAndPostTweet () {
  getComment()
    .then(function (results) {
      postTweet(results);
    })
    .catch(function (err) {
      console.log('ERROR:', err);
      makeAndPostTweet();
    });
}

function getComment () {

  return new Promise (function (resolve, reject) {

    console.log('\n---\n');

    // Pick a random comment. The API doesn't provide for this, but SoundCloud
    // comment IDs are sequential! There are a lot of missing comments (deleted
    // spam, etc.), but this has about a 40-50% success rate at finding an
    // actual comment, which is pretty good!
    var randomCommentID = String(_.random(100000000, 300000000));

    // Query the SoundCloud API and filter the results.
    SC.get('/comments/' + randomCommentID, function(err, comment) {

      // console.log('\n\nFULL API RESPONSE:\n\n', comment)

      console.log('LOOKING FOR A COMMENT AT ID #' + randomCommentID + '…');

      if (typeof(err) !== 'undefined') {
        reject('Comment does not exist at this ID anymore.');
      }
      else {
        var comment = comment.body.trim();

        console.log('FOUND A COMMENT:', comment);

        console.log('ANALYZING: Checking if too short…');

        if (comment.length < 1) {
          reject('Comment is too short.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if too long…');

        if (comment.length > 141) {
          reject('Comment is too long');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking for bad words…');

        if (wordfilter.blacklisted(comment)) {
          reject('Comment is a reply, contains a bad word, or looks like spam.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if is English…');

        if (!isEnglish(comment)) {
          reject('Comment is not in English.');
          return;
        }

        console.log('\tOK!');

        console.log('SUCCESS: All checks passed! Comment is useable!');
        resolve(comment);
      }

    });
  });
}

function postTweet (tweet) {

  if (typeof(tweet) !== 'undefined') {
    console.log('NOW TWEETING:', tweet);

    if (isProduction() || canTweetFromLocal) {
      twitter.post('statuses/update', { status: tweet }, function (error) {
        if (error) {
          console.log('ERROR POSTING TWEET:', error);
        }
      });
    }
  }
  else {
    console.log('ERROR: No comment was fetched!');
  }

}

// Tweet on a regular schedule. 8 times a day means every 3 hours. Note that
// Because Heroku cycles dynos once per day, the bot's schedule will not be
// regular: https://devcenter.heroku.com/articles/how-heroku-works#dyno-manager
if (isProduction()) {
  var timesToTweetPerDay = 8;

  setInterval(function () {
    try {
      makeAndPostTweet();
    }
    catch (err) {
      console.log('PROCESS UNSUCCESSFUL!', err);
    }
  }, (1000 * 60 * 60 * 24) / timesToTweetPerDay);
}

///

// Is this English?
function isEnglish (comment) {

  var options = {
    isHTML       : false,
    encodingHint : 'UTF8UTF8',
    tldHint      : 'com'
  };

  return languageDetector.detect(comment, options, function(err, result) {

    if (!_.isEmpty(err)) {
      console.log('LANGUAGE DETECTION ERROR:', err.message);

      // If the language couldn't be detected, just show the tweet. Most of the
      // time this means it's slang or internet-speak and is fine to print.
      if (err.message === 'Failed to identify language') {
        console.log('LANGUAGE DETECTION OVERRIDE: Approving as English anyway, just for kicks.');

        return true;
      }
    }
    else {
      return (result.languages[0].name === 'ENGLISH');
    }

  });

}

// Additional filters.
wordfilter.addWords([

  // To get a better set of comments, I'm filtering out a lot of things.
  // The lists below are used in addition to the default set:
  // https://github.com/dariusk/wordfilter/blob/master/lib/badwords.json

  // Promotion.
  'follow',
  'listen to my',
  'check out',
  'check my',
  'subscribe',
  'channel',
  'my cloud',
  'add me',

  'soundcloud',
  'facebook',
  'twitter',
  'youtube',
  'instagram',
  'blog',

  'free',
  'download',

  // Fake comments.
  'repost',
  'posted',
  'full support',
  'fully support',

  // Traditional spam.
  'sex',
  'cam',
  'dollars',
  'money',

  // URLs.
  'http',
  'www',
  '.co',
  '.net',

  // Replies, SoundCloud links, tags, etc.
  'user-',
  '\@',
  '\n',
  '_',
  '#',

  // A few curses and bad words.
  'fuck',
  'rape'

]);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Steven Soloff
 *
 * 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.
 */

"use strict";

var request = require("request");

function EvaluateService() {
    this.request = {};
}

EvaluateService.prototype.call = function (callback) {
    var requestData = {
        form: this.request,
        uri: "http://localhost:3000/evaluate"
    };
    request.post(requestData, function (error, response, body) {
        if (!error && (response.statusCode === 200)) {
            callback(JSON.parse(body));
        } else {
            throw new Error("unexpected response from evaluate service");
        }
    });
};

EvaluateService.prototype.setExpression = function (expressionText) {
    this.request.expression = {
        text: expressionText
    }
};

EvaluateService.prototype.setRandomNumberGenerator = function (randomNumberGeneratorName) {
    this.request.randomNumberGenerator = {
        name: randomNumberGeneratorName
    };
};

module.exports = EvaluateService;

<commit_msg>Add missing semi-colon.<commit_after>/*
 * Copyright (c) 2015 Steven Soloff
 *
 * 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.
 */

"use strict";

var request = require("request");

function EvaluateService() {
    this.request = {};
}

EvaluateService.prototype.call = function (callback) {
    var requestData = {
        form: this.request,
        uri: "http://localhost:3000/evaluate"
    };
    request.post(requestData, function (error, response, body) {
        if (!error && (response.statusCode === 200)) {
            callback(JSON.parse(body));
        } else {
            throw new Error("unexpected response from evaluate service");
        }
    });
};

EvaluateService.prototype.setExpression = function (expressionText) {
    this.request.expression = {
        text: expressionText
    };
};

EvaluateService.prototype.setRandomNumberGenerator = function (randomNumberGeneratorName) {
    this.request.randomNumberGenerator = {
        name: randomNumberGeneratorName
    };
};

module.exports = EvaluateService;

<|endoftext|>


<commit_before>var _ = require('underscore');
var S3 = require('tilelive-s3');
var url = require('url');
var path = require('path');
var iconv = new require('iconv').Iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE');

module.exports = S3;

// Converts a doc into an array of search terms.
// Terms that are part of a larger phrase are suffixed with an '-' indicating
// that they do not represent the complete text of the document, e.g.
//
//   united states =>
//     united-
//     states-
//     united_states
//
// A full normalized version of the doc is ensured to be the last entry making
// it possible to .pop() a normalized string usable as a search query prefix.
S3.terms = function(doc) {
    var terms = [];
    doc.split(',').forEach(function(doc) {
        var parts = doc.split(' ')
            .map(function(w) { return w.replace(/[^\w]/g, '').toLowerCase(); })
            .filter(function(w) { return w.length });
        terms = terms
            .concat(parts
                .filter(function(w) { return w.length > 1 })
                .map(function(w) { return parts.length > 1 ? w + '-' : w }))
            .concat(parts.length > 1 ? parts.join('_') : []);
    });
    return _(terms).uniq();
};

// Implements carmen#search method.
S3.prototype.search = function(query, id, callback) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    new S3.get({
        uri: url.format({
            hostname:uri.hostname,
            protocol:uri.protocol,
            query:{prefix:path.join(uri.pathname, 'term/' + S3.terms(query).pop()).substr(1)}
        }),
        headers: {Connection:'Keep-Alive'},
        agent: S3.agent
    }).asBuffer(function(err, buffer) {
        if (err) return callback(err);
        var xml = buffer.toString('utf8');
        var parsed = xml.match(new RegExp('[^>]+(?=<\\/Key>)', 'g')) || [];
        var docs = _(parsed).chain()
            .reduce(function(memo, obj) {
                var key = obj.split('/').pop().split('.');
                memo[key[1]] = memo[key[1]] || { text:[] };
                memo[key[1]].id = key[1];
                memo[key[1]].zxy = (memo[key[1]].zxy || [])
                    .concat(key.slice(2).map(function(v) { return v.replace(/,/g,'/') }));
                memo[key[1]].text.push(key[0].replace(/_/g,' '));
                return memo;
            }, {})
            .map(function(res) {
                res.text = _(res.text).uniq().join(',');
                return res;
            })
            .value();
        return callback(null, docs);
    }.bind(this));
};

// Implements carmen#feature method.
S3.prototype.feature = function(id, callback, raw) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    uri.pathname = path.join(uri.pathname, 'data/' + id + '.json');
    new S3.get({
        uri: url.format(uri),
        headers: {Connection:'Keep-Alive'},
        agent: S3.agent
    }).asBuffer(function(err, buffer) {
        if (err) return callback(err);
        try { var data = JSON.parse(buffer.toString('utf8')); }
        catch (err) { return callback(err); }
        if (!raw) delete data._terms;
        return callback(null, data);
    });
};

// Implements carmen#index method.
S3.prototype.index = function(id, text, doc, zxy, callback) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));
    if (!this.client) return callback(new Error('Tilesource not writing'));

    // @TODO get existing document and purge its terms.

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    try { text = iconv.convert(text).toString(); }
    catch(err) { return callback(err); }

    // Add each search term shard.
    var terms = [];
    var puts = [];
    var clean = text.replace(/[^\w,]/g,'').replace(/ /g,'_');
    S3.terms(text).forEach(function(shard) {
        var coords = zxy.map(function(zxy) { return zxy.replace(/\//g,',') });
        var prefix = path.join(uri.pathname, 'term/' + shard + '.' + clean + '.' + id + '.');
        while (coords.length) {
            var chunk = [];
            do {
                var next = prefix + chunk.join('.') + (coords.length ? '.' + coords[0] : '');
            } while (
                next.length < 1024 &&
                coords.length &&
                chunk.push(coords.shift())
            );
            var key = prefix + chunk.join('.');
            terms.push(key);
            puts.push({ key:key, data:'' });
        }
    });

    // Add actual doc to queue.
    puts.push({
        key:path.join(uri.pathname, 'data/' + id + '.json'),
        data:JSON.stringify(_({_terms:terms}).defaults(doc))
     });

    var put = function(err) {
        if (!puts.length) return callback();
        if (err) {
            console.error(err);
            return setTimeout(put, 500);
        }

        var obj = puts[0];
        var req = this.client.put(obj.key, {
            'x-amz-acl': 'public-read',
            'Connection': 'keep-alive',
            'Content-Length': obj.data ? Buffer.byteLength(obj.data, 'utf8') : 0,
            'Content-Type': 'application/json'
        });
        req.on('close', put);
        req.on('error', put);
        req.setTimeout(60e3, function() {
            req.abort();
            var err = new Error('ESOCKETTIMEDOUT');
            err.code = 'ESOCKETTIMEDOUT';
            req.emit('error', err);
        });
        req.on('response', function(res) {
            res.on('error', put);
            if (res.statusCode === 200) {
                puts.shift();
                return put();
            } else {
                console.warn(res);
                return put(new Error('S3 put failed: ' + res.statusCode));
            }
        });
        req.end(obj.data);
    }.bind(this);

    put();
};

// Implements carmen#indexable method.
S3.prototype.indexable = function(pointer, callback) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    pointer = pointer || null;

    // Pointer true means all docs have been read.
    if (pointer === true) return callback(null, [], pointer);

    new S3.get({
        uri: url.format({
            hostname:uri.hostname,
            protocol:uri.protocol,
            query:{
                marker: pointer,
                prefix: path.join(uri.pathname, 'data').substr(1),
                'max-keys':1000
            }
        }),
        headers: {Connection:'Keep-Alive'},
        agent: S3.agent
    }).asBuffer(function(err, buffer) {
        if (err) return callback(err);
        var xml = buffer.toString('utf8');
        var parsed = xml.match(new RegExp('[^>]+(?=<\\/Key>)', 'g')) || [];
        var truncated = /true<\/IsTruncated>/ig.test(xml);
        if (truncated) {
            pointer = parsed[parsed.length-1];
        } else {
            pointer = true;
        }

        // No more results.
        if (!parsed.length) return callback(null, [], pointer);

        var docs = [];
        var next = function() {
            if (!parsed.length) return callback(null, docs, pointer);
            var key = parsed[0];
            new S3.get({
                uri: url.format({
                    hostname:uri.hostname,
                    protocol:uri.protocol,
                    pathname:key
                }),
                headers: {Connection:'Keep-Alive'},
                agent: S3.agent
            }).asBuffer(function(err, buffer) {
                // @TODO consider retry here.
                if (err) return callback(err);

                try { var data = JSON.parse(buffer.toString('utf8')); }
                catch(err) { return callback(err); }

                var zxy = data._terms && data._terms.length &&
                    data._terms[0].split('/').pop().split('.').slice(2)
                    .map(function(zxy) { return zxy.replace(/,/g,'/') });
                var doc = {};
                doc.id = path.basename(key, path.extname(key));
                doc.doc = data;
                doc.text = data.search;
                doc.zxy = zxy || [];
                delete data._terms;

                docs.push(doc);
                parsed.shift();
                next();
            });
        };
        next();
    }.bind(this));
};

<commit_msg>Revert "Include cleaned doc text in term key."<commit_after>var _ = require('underscore');
var S3 = require('tilelive-s3');
var url = require('url');
var path = require('path');
var iconv = new require('iconv').Iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE');

module.exports = S3;

// Converts a doc into an array of search terms.
// Terms that are part of a larger phrase are suffixed with an '-' indicating
// that they do not represent the complete text of the document, e.g.
//
//   united states =>
//     united-
//     states-
//     united_states
//
// A full normalized version of the doc is ensured to be the last entry making
// it possible to .pop() a normalized string usable as a search query prefix.
S3.terms = function(doc) {
    var terms = [];
    doc.split(',').forEach(function(doc) {
        var parts = doc.split(' ')
            .map(function(w) { return w.replace(/[^\w]/g, '').toLowerCase(); })
            .filter(function(w) { return w.length });
        terms = terms
            .concat(parts
                .filter(function(w) { return w.length > 1 })
                .map(function(w) { return parts.length > 1 ? w + '-' : w }))
            .concat(parts.length > 1 ? parts.join('_') : []);
    });
    return _(terms).uniq();
};

// Implements carmen#search method.
S3.prototype.search = function(query, id, callback) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    new S3.get({
        uri: url.format({
            hostname:uri.hostname,
            protocol:uri.protocol,
            query:{prefix:path.join(uri.pathname, 'term/' + S3.terms(query).pop()).substr(1)}
        }),
        headers: {Connection:'Keep-Alive'},
        agent: S3.agent
    }).asBuffer(function(err, buffer) {
        if (err) return callback(err);
        var xml = buffer.toString('utf8');
        var parsed = xml.match(new RegExp('[^>]+(?=<\\/Key>)', 'g')) || [];
        var docs = _(parsed).chain()
            .reduce(function(memo, obj) {
                var key = obj.split('/').pop().split('.');
                memo[key[1]] = memo[key[1]] || { text:[] };
                memo[key[1]].id = key[1];
                memo[key[1]].zxy = (memo[key[1]].zxy || [])
                    .concat(key.slice(2).map(function(v) { return v.replace(/,/g,'/') }));
                memo[key[1]].text.push(key[0].replace(/_/g,' '));
                return memo;
            }, {})
            .map(function(res) {
                res.text = _(res.text).uniq().join(',');
                return res;
            })
            .value();
        return callback(null, docs);
    }.bind(this));
};

// Implements carmen#feature method.
S3.prototype.feature = function(id, callback, raw) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    uri.pathname = path.join(uri.pathname, 'data/' + id + '.json');
    new S3.get({
        uri: url.format(uri),
        headers: {Connection:'Keep-Alive'},
        agent: S3.agent
    }).asBuffer(function(err, buffer) {
        if (err) return callback(err);
        try { var data = JSON.parse(buffer.toString('utf8')); }
        catch (err) { return callback(err); }
        if (!raw) delete data._terms;
        return callback(null, data);
    });
};

// Implements carmen#index method.
S3.prototype.index = function(id, text, doc, zxy, callback) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));
    if (!this.client) return callback(new Error('Tilesource not writing'));

    // @TODO get existing document and purge its terms.

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    try { text = iconv.convert(text).toString(); }
    catch(err) { return callback(err); }

    // Add each search term shard.
    var terms = [];
    var puts = [];
    S3.terms(text).forEach(function(shard) {
        var coords = zxy.map(function(zxy) { return zxy.replace(/\//g,',') });
        var prefix = path.join(uri.pathname, 'term/' + shard + '.' + id + '.');
        while (coords.length) {
            var chunk = [];
            do {
                var next = prefix + chunk.join('.') + (coords.length ? '.' + coords[0] : '');
            } while (
                next.length < 1024 &&
                coords.length &&
                chunk.push(coords.shift())
            );
            var key = prefix + chunk.join('.');
            terms.push(key);
            puts.push({ key:key, data:'' });
        }
    });

    // Add actual doc to queue.
    puts.push({
        key:path.join(uri.pathname, 'data/' + id + '.json'),
        data:JSON.stringify(_({_terms:terms}).defaults(doc))
     });

    var put = function(err) {
        if (!puts.length) return callback();
        if (err) {
            console.error(err);
            return setTimeout(put, 500);
        }

        var obj = puts[0];
        var req = this.client.put(obj.key, {
            'x-amz-acl': 'public-read',
            'Connection': 'keep-alive',
            'Content-Length': obj.data ? Buffer.byteLength(obj.data, 'utf8') : 0,
            'Content-Type': 'application/json'
        });
        req.on('close', put);
        req.on('error', put);
        req.setTimeout(60e3, function() {
            req.abort();
            var err = new Error('ESOCKETTIMEDOUT');
            err.code = 'ESOCKETTIMEDOUT';
            req.emit('error', err);
        });
        req.on('response', function(res) {
            res.on('error', put);
            if (res.statusCode === 200) {
                puts.shift();
                return put();
            } else {
                console.warn(res);
                return put(new Error('S3 put failed: ' + res.statusCode));
            }
        });
        req.end(obj.data);
    }.bind(this);

    put();
};

// Implements carmen#indexable method.
S3.prototype.indexable = function(pointer, callback) {
    if (!this.data) return callback(new Error('Tilesource not loaded'));

    // Parse carmen URL.
    try { var uri = url.parse(this.data._carmen); }
    catch (err) { return callback(new Error('Carmen not supported')); }

    pointer = pointer || null;

    // Pointer true means all docs have been read.
    if (pointer === true) return callback(null, [], pointer);

    new S3.get({
        uri: url.format({
            hostname:uri.hostname,
            protocol:uri.protocol,
            query:{
                marker: pointer,
                prefix: path.join(uri.pathname, 'data').substr(1),
                'max-keys':1000
            }
        }),
        headers: {Connection:'Keep-Alive'},
        agent: S3.agent
    }).asBuffer(function(err, buffer) {
        if (err) return callback(err);
        var xml = buffer.toString('utf8');
        var parsed = xml.match(new RegExp('[^>]+(?=<\\/Key>)', 'g')) || [];
        var truncated = /true<\/IsTruncated>/ig.test(xml);
        if (truncated) {
            pointer = parsed[parsed.length-1];
        } else {
            pointer = true;
        }

        // No more results.
        if (!parsed.length) return callback(null, [], pointer);

        var docs = [];
        var next = function() {
            if (!parsed.length) return callback(null, docs, pointer);
            var key = parsed[0];
            new S3.get({
                uri: url.format({
                    hostname:uri.hostname,
                    protocol:uri.protocol,
                    pathname:key
                }),
                headers: {Connection:'Keep-Alive'},
                agent: S3.agent
            }).asBuffer(function(err, buffer) {
                // @TODO consider retry here.
                if (err) return callback(err);

                try { var data = JSON.parse(buffer.toString('utf8')); }
                catch(err) { return callback(err); }

                var zxy = data._terms && data._terms.length &&
                    data._terms[0].split('/').pop().split('.').slice(2)
                    .map(function(zxy) { return zxy.replace(/,/g,'/') });
                var doc = {};
                doc.id = path.basename(key, path.extname(key));
                doc.doc = data;
                doc.text = data.search;
                doc.zxy = zxy || [];
                delete data._terms;

                docs.push(doc);
                parsed.shift();
                next();
            });
        };
        next();
    }.bind(this));
};

<|endoftext|>