text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <title>JSDoc: Source: controller.js</title>

    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">

Source: controller.js

define([
  'openhangout.logging',
  'erizo',
  'async',
  'faye',
  'js.cookie',
  'fullscreen',
  'jquery.hotkeys',
  'openhangout.plugin.room.licode',
  'openhangout.plugin.room.janus',
], function(logging, Erizo, async, Faye, Cookies) {

  var ControllerFactory = function(RoomApp) {
    RoomApp.module("Controller", function(MODULE, APP, Backbone, Marionette, $, _) {

      var INSTANCES = {};

      /**
       *
       * Create a new instance of the room app.
       *
       * @constructor
       * @alias Controller.makeInstance
       *
       * @param {string} name
       *   Name for the instance.
       */
      MODULE.makeInstance = function(name) {
        name = name ? name : 'default';
        if (INSTANCES[name]) {
          return INSTANCES[name];
        }
        else {
          var instance = makeNewInstance(name);
          return instance;
        }
      }

      APP.reqres = new Backbone.Wreqr.RequestResponse();
      var makeNewInstance = function(name) {

        var log = logging(name);

        var INSTANCE = {};
        INSTANCE.inited = false;
        INSTANCE._instance = name;
        INSTANCE.EventBus = Backbone.Wreqr.radio.channel('openhangout-' + name);
        INSTANCE.Vent = INSTANCE.EventBus.vent;

        INSTANCE.Audio = APP.Audio.makeInstance(name);

        INSTANCE.settings = {};
        INSTANCE.configuration = _.extend({}, APP.Constants);

        INSTANCE.RegionManager = new Marionette.RegionManager();
        INSTANCE.Regions = INSTANCE.RegionManager.addRegions({
          headerButtons: '#header-buttons',
          fullscreenButtons: '#fullscreen-buttons',
          chat: '#chat',
        });
        INSTANCE.refreshRegions = function() {
          INSTANCE.Regions = INSTANCE.RegionManager.getRegions();
        }

        INSTANCE.muteIfLocalUser = function(userId) {
          if (userId == INSTANCE.settings.userId) {
            var muted = INSTANCE.AppState.get('localAudioMuted');
            if (!muted) {
              log.debug('AUDIO', 'setting localAudioMuted for local user');
              INSTANCE.AppState.set('localAudioMuted', true);
            }
          }
        };

        INSTANCE.changeLocalAudioMute = function() {
          var muted = INSTANCE.AppState.get('localAudioMuted');
          var result = INSTANCE.plugin.room.changeLocalAudioMute(muted);
          // Verify that all streams are active before allowing button press.
          if (result) {
            INSTANCE.changeAudioMute(INSTANCE.settings.userId, muted);
            if (muted) {
              INSTANCE.setVolumeToSilence();
            }
            INSTANCE.volumeEventsEnabled = !muted;
          }
        }

        INSTANCE.changeAudioMute = function(userId, muted) {
          var audioFeed = INSTANCE.AudioFeeds.get(userId);
          var videoFeed = INSTANCE.VideoFeeds.get(userId);
          if (audioFeed || videoFeed) {
            log.debug('AUDIO', 'setting isAudioMuted for feed:', userId, muted);
            audioFeed && audioFeed.set('isAudioMuted', muted);
            videoFeed && videoFeed.set('isAudioMuted', muted);
          }
        }

        INSTANCE.changeLocalVideoMute = function() {
          var muted = INSTANCE.AppState.get('localVideoMuted');
          var result = INSTANCE.plugin.room.changeLocalVideoMute(muted);
          if (result) {
            INSTANCE.changeVideoMute(INSTANCE.settings.userId, muted);
          }
        }

        INSTANCE.changeVideoMute = function(userId, muted) {
          var audioFeed = INSTANCE.AudioFeeds.get(userId);
          var videoFeed = INSTANCE.VideoFeeds.get(userId);
          if (audioFeed || videoFeed) {
            log.debug('VIDEO', 'setting isVideoMuted for feed:', userId, muted);
            audioFeed && audioFeed.set('isVideoMuted', muted);
            videoFeed && videoFeed.set('isVideoMuted', muted);
          }
        }

        INSTANCE.clearFocusUser = function(feed) {
          log.debug('AUTO FOCUS', 'clearing focus user');
          var attrs = {
            active: false,
            hasFocus: false,
          };
          INSTANCE.VideoFeeds.each(function(f) {
            if (f.id !== feed.id) {
              f.set(attrs);
            }
          });
        }

        INSTANCE.setFocusUser = function(feed) {
          INSTANCE.clearFocusUser(feed);
          // Examine state just prior to flipping.
          var autoFocus;
          var active = feed.get('active');
          if (active) {
            autoFocus = true;
          }
          else {
            autoFocus = false;
            feed.set('hasFocus', true);
          }
          log.debug('AUTO FOCUS', 'set focus user, active:', !active, 'autoFocus:', autoFocus);
          feed.set('active', !active);
          INSTANCE.AppState.set('autoFocus', autoFocus);
        }
        INSTANCE.Vent.on('focus:user', INSTANCE.setFocusUser);

        INSTANCE.setView = function(view) {
          Cookies.set('view', view, { expires: 1 });
          INSTANCE.AppState.set('view', view);
        };

        INSTANCE.switchView = function() {
          var oldView = INSTANCE.AppState.get('view');
          var newView = oldView == 'tile' ? 'focus' : 'tile';
          log.debug('VIEW', 'switch view to:', newView);
          INSTANCE.setView(newView);
        };
        INSTANCE.Vent.on('view:switch', INSTANCE.switchView);

        INSTANCE.setChatDisplay = function(isOpen) {
          INSTANCE.AppState.set('chat', isOpen);
          INSTANCE.AppState.set('chatUnread', 0);
          log.debug('CHAT', 'set chat display:', isOpen);
          INSTANCE.resizeLayout();
        }

        INSTANCE.toggleChat = function() {
          INSTANCE.setChatDisplay(!INSTANCE.AppState.get('chat'));
        };
        INSTANCE.Vent.on('chat:toggle', INSTANCE.toggleChat);

        INSTANCE.setFocusCropDisplay = function(crop) {
          INSTANCE.AppState.set('focusCrop', crop);
          log.debug('VIEW', 'set focus crop:', crop);
          INSTANCE.resizeLayout();
        }

        INSTANCE.toggleFocusCrop = function() {
          INSTANCE.setFocusCropDisplay(!INSTANCE.AppState.get('focusCrop'));
        };
        INSTANCE.Vent.on('view:focus:crop', INSTANCE.toggleFocusCrop);

        INSTANCE.openChat = function() {
          var isOpen = INSTANCE.AppState.get('chat');
          if (!isOpen) {
            INSTANCE.setChatDisplay(true);
          }
        };
        INSTANCE.Vent.on('chat:open', INSTANCE.openChat);

        INSTANCE.toggleLocalAudioMute = function() {
          INSTANCE.AppState.set('localAudioMuted', !INSTANCE.AppState.get('localAudioMuted'));
        };
        INSTANCE.Vent.on('audio:local:toggle', INSTANCE.toggleLocalAudioMute);

        INSTANCE.toggleLocalVideoMute = function() {
          INSTANCE.AppState.set('localVideoMuted', !INSTANCE.AppState.get('localVideoMuted'));
        };
        INSTANCE.Vent.on('video:local:toggle', INSTANCE.toggleLocalVideoMute);

        INSTANCE.muteUser = function(model) {
          var data = {
            type: 'mute-user',
            userId: model.id,
          }
          log.debug('AUDIO', 'broadcast mute user:', data.userId);
          INSTANCE.RoomSocket.publish('/' + INSTANCE.settings.roomName + '/command', data);
        };
        INSTANCE.Vent.on('audio:mute:user', INSTANCE.muteUser);

        INSTANCE.refreshFeedsUser = function(model) {
          log.debug('FEED', 'refresh feeds user:', model.id);
          var postUnsubscribeCallback = function() {
            INSTANCE.removeUserFeeds(model.id);
          }
          INSTANCE.plugin.room.resubscribeStreams(model.id, postUnsubscribeCallback);
        };
        INSTANCE.Vent.on('refresh:feeds:user', INSTANCE.refreshFeedsUser);

        INSTANCE.setFullscreenListener = function() {
          var listener = function(jqEvt, active, el) {
            INSTANCE.setFullscreen(active);
            INSTANCE.setButtonsPosition(active);
          }
          $(document).on('fscreenchange', listener);
        };

        INSTANCE.getFullscreenState = function() {
          var fullscreen = $.fullscreen.isFullScreen();
          return fullscreen;
        };

        INSTANCE.setButtonsPosition = function(active) {
          if (active) {
            INSTANCE.placeButtons('fullscreenButtons', 'headerButtons');
          }
          else {
            INSTANCE.placeButtons('headerButtons', 'fullscreenButtons');
          }
        };

        INSTANCE.setFullscreen = function(active) {
          INSTANCE.AppState.set('fullscreen', active);
          INSTANCE.Vent.trigger('fullscreen:state', active);
        };

        INSTANCE.toggleFullscreen = function() {
          INSTANCE.setFullscreen(!INSTANCE.getFullscreenState());
        };
        INSTANCE.Vent.on('fullscreen:toggle', INSTANCE.toggleFullscreen);

        INSTANCE.hangupConnection = function() {
          // NOTE: This will fail unless the window was originally opened by the
          // user via javascript.
          log.info('APP', 'hangup');
          window.close();
        };
        INSTANCE.Vent.on('connection:hangup', INSTANCE.hangupConnection);

        INSTANCE.reloadConnection = function() {
          log.info('APP', 'reload');
          location.reload();
        };
        INSTANCE.Vent.on('connection:reload', INSTANCE.reloadConnection);

        INSTANCE.addChatToMessages = function(userId, data) {
          var audioFeed = INSTANCE.AudioFeeds.get(userId);
          var videoFeed = INSTANCE.VideoFeeds.get(userId);
          if (audioFeed) {
            var chatData = {
              username: data.username,
              message: data.message,
              feed: audioFeed,
            }
            log.debug('CHAT', 'adding chat to messages, username:', chatData.username, 'message:', chatData.message);
            var model = new APP.Model.ChatMessage(chatData);
            INSTANCE.ChatMessages.add(model);
            INSTANCE.storeChatMessage(model);
            var chatIsOpen = INSTANCE.AppState.get('chat');
            if (data.userId != INSTANCE.settings.userId) {
              if (INSTANCE.configuration.chatAlwaysShowPopups || !chatIsOpen) {
                videoFeed && videoFeed.set('chatPopoverMessage', data.message);
              }
            }
            if (!chatIsOpen) {
              var unread = INSTANCE.AppState.get('chatUnread');
              unread++;
              INSTANCE.AppState.set('chatUnread', unread);
            }
          }
        };

        INSTANCE.sendChatMessage = function(message) {
          var data = {
            userId: INSTANCE.settings.userId,
            username: INSTANCE.settings.username,
            message: message,
          }
          log.debug('CHAT', 'broadcasting chat message, username:', data.username, 'message:', data.message);
          INSTANCE.RoomSocket.publish('/' + INSTANCE.settings.roomName + '/chat', data);
        };

        INSTANCE.Vent.on('chat:message:send', INSTANCE.sendChatMessage);

        /**
         * Configure the application prior to connecting.
         *
         * @param {object} settings
         *   Settings to specify the user, published streams, etc.
         * @param {object} configuration
         *   Override core configuration values.
         */
        INSTANCE.configure = function(settings, configuration) {
          configuration = configuration ? configuration : {};
          log.debug('APP', 'configuring with settings', settings);
          INSTANCE.settings = settings;
          _.extend(INSTANCE.configuration, configuration);
        };

        INSTANCE.initApp = function() {
          if (_.isObject(INSTANCE.settings)) {
            log.info('APP', 'init');
            INSTANCE.initAppState();
            INSTANCE.initPlugins();
            INSTANCE.initRoomSocket();
            INSTANCE.initUserInfo();
            INSTANCE.initDisplay();
            INSTANCE.initChat();
            INSTANCE.initFeeds();
            INSTANCE.initAudioView();
            INSTANCE.initTileView();
            INSTANCE.initFilmstripView();
            INSTANCE.initFocusView();
            INSTANCE.initLayoutView();
            INSTANCE.configLocalAudioStream();
            INSTANCE.configLocalDisplayStream();
            INSTANCE.configLocalPublishedVideoStreams();
            INSTANCE.connect('presenter');
            INSTANCE.inited = true;
          }
          else {
            var message = "ERROR: missing application-level settings, call configure() to configure";
            log.error('APP', message);
            throw(message);
          }
        };

        INSTANCE.buildRoomPluginStreamConfigs = function() {
          var configs = {};
          if (INSTANCE.localAudioStreamConfig) {
            configs.audio = INSTANCE.localAudioStreamConfig;
          }
          if (INSTANCE.localDisplayStreamConfig) {
            configs.displayVideo = INSTANCE.localDisplayStreamConfig;
          }
          if (INSTANCE.localPublishedVideoStreamsConfig) {
            configs.publishedVideo = INSTANCE.localPublishedVideoStreamsConfig;
          }
          return configs;
        }

        INSTANCE.connect = function(role) {
          role = role ? role : 'presenter';
          var configs = INSTANCE.buildRoomPluginStreamConfigs();
          INSTANCE.plugin.room.connect(role, configs);
        }

        INSTANCE.connectError = function(err) {
          var errorType = err.type;
          var errorMessage = err.message;
          switch(errorType) {
            case 'stream':
              var mediaError = err.mediaError;
              var message = "Error accessing local mic/camera: ";
              var mediaErrorDetails;
              var name = 'unknown';
              var constrainName = 'unknown';
              try {
                mediaErrorDetails = mediaError[0][1];
                name = mediaErrorDetails.name;
                constraintName = mediaErrorDetails.constraintName;
              }
              catch (e) {
                mediaErrorDetails = ': unknown reason';
              }
              log.error('STREAM', message, errorMessage, mediaErrorDetails);
              alert(message + errorMessage + ", " + name + ": " + constraintName);
              break;
            case 'room':
              alert(errorMessage);
              break;
          }
        }

        INSTANCE.disconnect = function() {
          INSTANCE.disconnectRoom();
          INSTANCE.disconnectRoomSocket();
        }

        INSTANCE.roomDisconnected = function() {
          INSTANCE.Audio.teardown();
          INSTANCE.VideoFeeds.reset([]);
          INSTANCE.AudioFeeds.reset([]);
          INSTANCE.Room = null;
          INSTANCE.Vent.trigger('room:disconnected');
        }

        INSTANCE.getHeaderHeight = function() {
          if (INSTANCE.AppState.get('fullscreen')) {
            return 0;
          }
          else {
            return INSTANCE.configuration.headerHeight;
          }
        }

        INSTANCE.placeButtons = function(newRegion, clearRegion) {
          log.debug('DISPLAY', 'placing buttons from:', clearRegion, 'to:', newRegion);
          if (clearRegion) {
            INSTANCE.Regions[clearRegion].reset();
          }
          INSTANCE.ButtonsInstance = new APP.View.Buttons({
            model: INSTANCE.AppState,
            region: newRegion,
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          INSTANCE.Regions[newRegion].show(INSTANCE.ButtonsInstance);
        };

        INSTANCE.initAppState = function() {
          log.debug('APP', 'init state');
          INSTANCE.AppState = new APP.Model.App();
          INSTANCE.volumeEventsEnabled = !INSTANCE.configuration.audioMuteDefault;
        };

        INSTANCE.initPlugins = function() {
          log.debug('APP', 'init plugins');
          INSTANCE.plugin = {};
          var room = require('openhangout.plugin.room.' + INSTANCE.configuration.plugin.room);
          INSTANCE.plugin.room = new room(INSTANCE);
          log.info('APP', 'Room plugin init complete: ', INSTANCE._instance, INSTANCE.plugin.room.name);
        };

        INSTANCE.roomSocketCommandCallback = function(data) {
          switch (data.type) {
            case 'mute-user':
              INSTANCE.muteIfLocalUser(data.userId);
              break;
          }
        }

        INSTANCE.roomSocketChatCallback = function(data) {
          INSTANCE.addChatToMessages(data.userId, data);
        }

        INSTANCE.roomSocketVolumeCallback = function(data) {
          INSTANCE.setFeedVolume(data.userId, data.volume);
        }

        INSTANCE.initRoomSocket = function() {
          var hostname = INSTANCE.settings.videoServerHostname || INSTANCE.configuration.defaultVideoServerHostname;
          var port = INSTANCE.settings.videoServerPort || INSTANCE.configuration.defaultVideoServerPort;
          var roomName = INSTANCE.settings.roomName;
          INSTANCE.RoomSocket = new Faye.Client('https://' + hostname + ':' + port + '/faye', {retry: 5});
          INSTANCE.RoomSocket.subscribe('/' + roomName + '/command', INSTANCE.roomSocketCommandCallback);
          INSTANCE.RoomSocket.subscribe('/' + roomName + '/chat', INSTANCE.roomSocketChatCallback);
          INSTANCE.RoomSocket.subscribe('/' + roomName + '/volume', INSTANCE.roomSocketVolumeCallback);
        }

        INSTANCE.disconnectRoom = function() {
          INSTANCE.Room && INSTANCE.plugin.room.disconnect(INSTANCE.Room);
        }

        INSTANCE.disconnectRoomSocket = function() {
          INSTANCE.RoomSocket && INSTANCE.RoomSocket.disconnect();
        }

        INSTANCE.getChatMessagesFromStorage = function() {
          var roomName = INSTANCE.settings.roomName;
          var chatData = localStorage.getItem('chatData');
          var roomData;
          try {
            roomData = JSON.parse(chatData) || {};
          }
          catch(e) {
            roomData = {};
          }
          var chatMessages = roomData[roomName] || [];
          log.debug('CHAT', 'retrieved messages from storage', chatMessages);
          return chatMessages;
        };

        INSTANCE.storeChatData = function(chatMessages) {
          var roomName = INSTANCE.settings.roomName;
          var newRoomData = {}
          newRoomData[roomName] = chatMessages;
          log.debug('CHAT', 'storing messages', newRoomData);
          localStorage.setItem('chatData', JSON.stringify(newRoomData));
        }

        INSTANCE.storeChatMessage = function(model) {
          var chatMessages = INSTANCE.getChatMessagesFromStorage();
          var messageData = model.toJSON();
          // Extract only the feed data needed, the full model won't store.
          var feedData = messageData.feed.toJSON();
          messageData.feed = {
            chatColor: feedData.chatColor,
          }
          log.debug('CHAT', 'storing message', messageData);
          chatMessages.push(messageData);
          INSTANCE.storeChatData(chatMessages);
        };

        INSTANCE.initChat = function() {
          log.debug('CHAT', 'init');
          // This function is simplistic for now, reading stored chat messages for
          // the current room from an array keyed by room, then writing back only
          // that array key to storage to clear out messages from old rooms.
          var chatMessages = INSTANCE.getChatMessagesFromStorage();
          INSTANCE.storeChatData(chatMessages);
          // Must reconstitute as models, the rest of the code is expecting it.
          for (var key in chatMessages) {
            var feedData = chatMessages[key].feed;
            chatMessages[key] = new APP.Model.ChatMessage(chatMessages[key]);
            chatMessages[key].set('feed', new APP.Model.AudioFeed(feedData));
          }
          INSTANCE.ChatMessages = new APP.Collection.ChatMessages(chatMessages);
          INSTANCE.ChatInstance = new APP.View.Chat({
            model: INSTANCE.AppState,
            collection: INSTANCE.ChatMessages,
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          INSTANCE.Regions.chat.show(INSTANCE.ChatInstance);
        };

        INSTANCE.initUserInfo = function() {
          if (_.isEmpty(INSTANCE.settings.userId) || _.isEmpty(INSTANCE.settings.username)) {
            log.debug('USER INFO', 'init');
            var userId = Cookies.get('userId');
            var name = Cookies.get('username');
            if (!userId || !name) {
              var defaultUserId = String(Math.random()).substring(2);
              var defaultName = 'Anonymous user';
              Cookies.set('userId', defaultUserId, { expires: 1 });
              name = prompt('Please enter your name', defaultName);
              name && Cookies.set('username', name, { expires: 1 });
            }
            INSTANCE.settings.username = name ? name : defaultName;
            INSTANCE.settings.userId = userId ? userId : defaultUserId;
          }
        }

        INSTANCE.initDisplay = function() {
          log.debug('DISPLAY', 'init');
          INSTANCE.setFullscreenListener();
          var fullscreen = INSTANCE.getFullscreenState();
          INSTANCE.AppState.set('fullscreen', fullscreen);
          var placement = fullscreen ? 'fullscreenButtons' : 'headerButtons';
          INSTANCE.placeButtons(placement);

          INSTANCE.AppState.on('change:view', INSTANCE.setLayout);
          INSTANCE.AppState.on('change:localAudioMuted', INSTANCE.changeLocalAudioMute);
          INSTANCE.AppState.on('change:localVideoMuted', INSTANCE.changeLocalVideoMute);

          INSTANCE.hotkeys = INSTANCE.initHotkeys();
          if (INSTANCE.configuration.hotkeys) {
            INSTANCE.hotkeys.activate($(document));
          }
          $(window).resize(function() {
            INSTANCE.resizeLayout();
          });
        };

        INSTANCE.initHotkeys = function() {
          log.debug('HOTKEYS', 'init');

          // These allow the hotkeys to work even when an input has the
          // focus. Given the ctrl+shift prefix, this seems reasonable
          // and safe, and avoids inconsistencies when using the hotkeys.
          $.hotkeys.options.filterInputAcceptingElements = false;
          $.hotkeys.options.filterContentEditable = false;
          $.hotkeys.options.filterTextInputs = false;

          var makeHotkey = function(key) {
            var hotkey = 'ctrl+shift+' + key;
            return hotkey;
          };

          var hotkeysLog = function(event, funcName) {
            log.debug('HOTKEY', event.data.keys + ' hotkey pressed, calling: ' + funcName);
          };

          var toggleLocalAudioMute = function(data) {
            hotkeysLog(data, 'toggleLocalAudioMute');
            INSTANCE.toggleLocalAudioMute();
          };

          var toggleLocalVideoMute = function(data) {
            hotkeysLog(data, 'toggleLocalVideoMute');
            INSTANCE.toggleLocalVideoMute();
          };

          var toggleChat = function(data) {
            hotkeysLog(data, 'toggleChat');
            INSTANCE.toggleChat();
          };

          var switchView = function(data) {
            hotkeysLog(data, 'switchView');
            INSTANCE.switchView();
          };

          var toggleFullscreen = function(data) {
            hotkeysLog(data, 'toggleFullscreen');
            INSTANCE.toggleFullscreen();
          };

          var bindings = {
              m: toggleLocalAudioMute,
              v: toggleLocalVideoMute,
              c: toggleChat,
              s: switchView,
              f: toggleFullscreen,
          }

          var boundFunctions = {};

          var bindingsCallback = function(func, key) {
              var boundFunc = _.bind(func, this);
              boundFunctions[key] = boundFunc;
          }
          _.each(bindings, _.bind(bindingsCallback, this));

          var activate = function(el) {
              log.debug('HOTKEYS', 'activate');
              var callback = function(func, key) {
                  el.bind('keyup', makeHotkey(key), func);
              }
              _.each(boundFunctions, callback);
          }

          var deactivate = function(el) {
              log.debug('HOTKEYS', 'deactivate');
              var callback = function(func, key) {
                  el.unbind('keyup', func);
              }
              _.each(boundFunctions, callback);
          }

          log.debug('HOTKEYS', 'bindings', bindings);

          return {
            activate: activate,
            deactivate: deactivate,
          }
        };

        INSTANCE.setDefaultView = function() {
          var view = Cookies.get('view');
          log.debug('DISPLAY', 'set default view:', view);
          if (view) {
            INSTANCE.AppState.set('view', view);
            INSTANCE.setLayout();
          }
        };

        INSTANCE.setLayout = function() {
          var tileContainer = $(INSTANCE.configuration.tileViewContainer);
          var focusContainer = $(INSTANCE.configuration.focusViewContainer);
          var filmstripContainer = $(INSTANCE.configuration.filmstripViewContainer);
          var view = INSTANCE.AppState.get('view');
          switch (view) {
            // TODO: Rip out CSS positioning.
            // NOTE: The CSS positioning is an attempt to eliminate black
            // flickering of the video in Chrome.
            case 'tile':
              focusContainer.hide();
              focusContainer.css({'position': 'absolute', 'left': 10000});
              filmstripContainer.hide();
              filmstripContainer.css({'left': 10000});
              // Clears all from focus.
              INSTANCE.clearFocusUser({});
              tileContainer.css({'position': '', 'left': ''});
              tileContainer.show();
              break;
            case 'focus':
              tileContainer.hide();
              tileContainer.css({'position': 'absolute', 'left': 10000});
              INSTANCE.setBestFocus();
              focusContainer.css({'position': '', 'left': ''});
              focusContainer.show();
              filmstripContainer.css('left', '');
              filmstripContainer.show();
              break;
          }
          log.debug('DISPLAY', 'set layout:', view);
          INSTANCE.resizeLayout();
        };

        INSTANCE.initFeeds = function() {
          log.debug('FEED', 'init');
          INSTANCE.VideoFeeds = new APP.Collection.VideoFeeds();
          INSTANCE.VideoFeeds.on('add', INSTANCE.videoFeedAdded);
          INSTANCE.VideoFeeds.on('remove', INSTANCE.videoFeedRemoved);
          INSTANCE.AudioFeeds = new APP.Collection.AudioFeeds();
          INSTANCE.AudioFeeds.on('remove', INSTANCE.audioFeedRemoved);
        }

        INSTANCE.initAudioView = function() {
          log.debug('DISPLAY', 'init audio view');
          INSTANCE.RegionManager.addRegion('audioContainer', INSTANCE.configuration.audioViewContainer);
          INSTANCE.refreshRegions();
          INSTANCE.UserAudioStreamsInstance = new APP.View.UserAudioStreams({
            collection: INSTANCE.AudioFeeds,
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          INSTANCE.Regions.audioContainer.show(INSTANCE.UserAudioStreamsInstance);
        };

        INSTANCE.initTileView = function() {
          log.debug('DISPLAY', 'init tile view');
          INSTANCE.RegionManager.addRegion('tileContainer', INSTANCE.configuration.tileViewContainer);
          INSTANCE.refreshRegions();
          INSTANCE.UserVideosTileInstance = new APP.View.UserVideosTile({
            collection: INSTANCE.VideoFeeds,
            videoType: 'tile',
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          INSTANCE.Regions.tileContainer.show(INSTANCE.UserVideosTileInstance);
        };

        INSTANCE.initFilmstripView = function() {
          log.debug('DISPLAY', 'init filmstrip view');
          INSTANCE.RegionManager.addRegion('filmstripContainer', INSTANCE.configuration.filmstripViewContainer);
          INSTANCE.refreshRegions();
          INSTANCE.UserVideosFilmstripInstance = new APP.View.UserVideosFilmstrip({
            collection: INSTANCE.VideoFeeds,
            videoType: 'filmstrip',
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          INSTANCE.Regions.filmstripContainer.show(INSTANCE.UserVideosFilmstripInstance);
        };

        INSTANCE.initFocusView = function() {
          log.debug('DISPLAY', 'init focus view');
          INSTANCE.RegionManager.addRegion('focusContainer', INSTANCE.configuration.focusViewContainer);
          INSTANCE.refreshRegions();
          INSTANCE.UserVideosFocusInstance = new APP.View.UserVideosFocus({
            collection: INSTANCE.VideoFeeds,
            videoType: 'focus',
            appState: INSTANCE.AppState,
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          INSTANCE.Regions.focusContainer.show(INSTANCE.UserVideosFocusInstance);
        };

        INSTANCE.initLayoutView = function() {
          INSTANCE.LayoutInstance = new APP.View.Layout({
            model: INSTANCE.AppState,
            vent: INSTANCE.Vent,
            settings: INSTANCE.settings,
            configuration: INSTANCE.configuration,
            instance: INSTANCE._instance,
          });
          var displayFullscreenButtons = function() {
            if (INSTANCE.AppState.get('fullscreen')) {
              INSTANCE.LayoutInstance.fullscreenButtonsShow();
            }
          }
          INSTANCE.Vent.on('fullscreen:buttons:display', displayFullscreenButtons);
          INSTANCE.setDefaultView();
        };

        INSTANCE.configLocalPublishedVideoStream = function(config, streamGroup) {
          log.debug('STREAM', 'configure video stream for publish to:', streamGroup, config);
          INSTANCE.localPublishedVideoStreamsConfig = INSTANCE.localPublishedVideoStreamsConfig ? INSTANCE.localPublishedVideoStreamsConfig : {};
          var videoStreamConfig = INSTANCE.videoStreamConfig(config, streamGroup);
          INSTANCE.localPublishedVideoStreamsConfig[streamGroup] = videoStreamConfig;
        }

        INSTANCE.configLocalPublishedVideoStreams = function() {
          log.debug('STREAM', 'config video streams');
          INSTANCE.localPublishedVideoStreams = {};
          var publishStreamGroups = INSTANCE.settings.publishStreamGroups;
          if (!publishStreamGroups) {
            publishStreamGroups = {};
            publishStreamGroups[INSTANCE.configuration.streamGroupDefault] = { publishedFor: [INSTANCE.configuration.streamGroupDefault] };
          }
          for (var key in publishStreamGroups) {
            INSTANCE.configLocalPublishedVideoStream(publishStreamGroups[key], key);
          }
          INSTANCE.publishingForGroups = INSTANCE.calculatePublishedFor(publishStreamGroups);
        }

        INSTANCE.videoStreamAttrs = function(videoAttrs, streamAttrs) {
          // TODO: Figure out screen sharing? Disabled for now.
          // var screen = APP.Util.getParameterByName("screen");
          var screen = false;
          var config = {
            audio: false,
            video: {
              mandatory: {
                maxWidth: videoAttrs.width,
                maxHeight: videoAttrs.height,
              },
            },
            data: false,
            screen: screen,
            attributes: streamAttrs,
          };
          // For screen sharing a Chrome extension ID is required.
          // If not using Chrome, the creation of the stream will fail.
          if (screen && INSTANCE.settings.chromeScreenshareExtensionId){
            config.extensionId = INSTANCE.settings.chromeScreenshareExtensionId;
          }
          log.debug('STREAM', 'config', config);
          return config;
        };

        INSTANCE.configLocalAudioStream = function(config) {
          if (!config) {
            config = {
              audio: {
                optional: [
                  { googEchoCancellation: INSTANCE.configuration.googleEchoCancellation },
                  { googAutoGainControl: INSTANCE.configuration.googleAutoGainControl },
                  { googNoiseSuppression: INSTANCE.configuration.googleNoiseSuppression },
                  { googHighpassFilter: INSTANCE.configuration.googleHighpassFilter },
                  { googTypingNoiseDetection: INSTANCE.configuration.googleTypingNoiseDetection },
                  { googEchoCancellation2: INSTANCE.configuration.googleEchoCancellation2 },
                  { googAutoGainControl2: INSTANCE.configuration.googleAutoGainControl2 },
                ],
              },
              video: false,
              data: false,
              screen: false,
              attributes: {
                streamGroup: 'audio',
                isAudioStream: true,
                username: INSTANCE.settings.username,
                userId: INSTANCE.settings.userId,
                isAudioMuted: INSTANCE.configuration.audioMuteDefault,
                isVideoMuted: INSTANCE.configuration.videoMuteDefault,
              },
            };
          }
          log.debug('STREAM', 'config local audio stream', config);
          INSTANCE.localAudioStreamConfig = config;
        };

        INSTANCE.configLocalDisplayStream = function(config) {
          if (!config) {
            var videoAttrs;
            if (INSTANCE.settings.localVideo) {
              videoAttrs = {
                width: INSTANCE.settings.localVideo.width,
                height: INSTANCE.settings.localVideo.height,
              };
            }
            else {
              videoAttrs = {
                width: INSTANCE.configuration.localDisplayVideoWidthDefault,
                height: INSTANCE.configuration.localDisplayVideoHeightDefault,
              };
            }
            var streamAttrs = {
              streamGroup: 'local',
              isVideoStream: true,
              username: INSTANCE.settings.username,
              userId: INSTANCE.settings.userId,
              isAudioMuted: INSTANCE.configuration.audioMuteDefault,
              isVideoMuted: INSTANCE.configuration.videoMuteDefault,
            };
            config = INSTANCE.videoStreamAttrs(videoAttrs, streamAttrs);
          }
          log.debug('STREAM', 'config local display stream', config);
          INSTANCE.localDisplayStreamConfig = config;
        };

        INSTANCE.videoStreamConfig = function(config, streamGroup) {
          streamGroup = streamGroup ? streamGroup : INSTANCE.configuration.streamGroupDefault;
          var videoAttrs;
          if (config && config.videoSize) {
            videoAttrs = {
              width: config.videoSize.width,
              height: config.videoSize.height,
            };
          }
          else {
            videoAttrs = {
              width: INSTANCE.configuration.videoStreamWidthDefault,
              height: INSTANCE.configuration.videoStreamHeightDefault,
            };
          }
          var publishedFor;
          if (config && config.publishedFor) {
            publishedFor = config.publishedFor;
          }
          else {
            publishedFor = [INSTANCE.configuration.streamGroupDefault];
          }
          var streamAttrs = {
            streamGroup: streamGroup,
            publishedFor: publishedFor,
            isVideoStream: true,
            username: INSTANCE.settings.username,
            userId: INSTANCE.settings.userId,
            isAudioMuted: INSTANCE.configuration.audioMuteDefault,
            isVideoMuted: INSTANCE.configuration.videoMuteDefault,
          };
          return INSTANCE.videoStreamAttrs(videoAttrs, streamAttrs);
        };

        INSTANCE.createUserToken = function(role, callback) {
          log.debug('TOKEN', 'start');
          callback = callback ? callback : function() {};
          var userId = INSTANCE.settings.userId;
          var count = 1;
          var timeout = INSTANCE.configuration.serverRetryInterval;
          var createUserTokenCallback = function (err, token) {
            log.debug('TOKEN', token);
            if (err) {
              log.error('TOKEN', 'error', err);
              count++;
              if (count > INSTANCE.configuration.serverRetries) {
                log.error('TOKEN', 'error, maximum server retries exceeded', INSTANCE.configuration.serverRetries);
                callback(err);
              }
              else {
                timeout += INSTANCE.configuration.serverRetryInterval;
                setTimeout(function() {
                  INSTANCE._createUserToken(userId, role, createUserTokenCallback);
                }, timeout);
              }
            }
            else {
              log.info('TOKEN', 'complete');
              callback(null, token);
            }
          }
          INSTANCE._createUserToken(userId, role, createUserTokenCallback);
        };

        INSTANCE._createUserToken = function(userId, role, callback) {
          log.debug('TOKEN', 'creating');

          var url = '/room/' + INSTANCE.settings.roomName + '/create-token/';
          var body = {
            userId: userId,
            role: role
          };

          var settings = {
            cache: false,
            contentType: 'application/json; charset=UTF-8',
            data: JSON.stringify(body),
            dataType: 'text',
            type: 'POST',
          }

          $.ajax(url, settings)
            .done(function(data) {
              callback(null, data);
            })
            .fail(function(jqXHR, textStatus, errorThrown) {
              var message = 'Error fetching token';
              log.error('TOKEN', message, textStatus, errorThrown);
              callback(message);
            });
        };

        // This ensures that the first two users get a sensible focus state, and
        // that somebody gets the focus if the view is switched to before anyone has
        // the focus. All others coming in should respond to talking events.
        INSTANCE.setBestFocus = function() {
          var active = INSTANCE.VideoFeeds.findWhere({active: true});
          var hasFocus = INSTANCE.VideoFeeds.findWhere({hasFocus: true});
          if ((!active && INSTANCE.VideoFeeds.length < 3) || !hasFocus) {
            INSTANCE.focusLast();
          }
        };

        INSTANCE.focusLast = function() {
          // Take the last joined for focus.
          if (INSTANCE.AppState.get('view') === 'focus' && INSTANCE.VideoFeeds.length) {
            var feed = INSTANCE.VideoFeeds.models[INSTANCE.VideoFeeds.length - 1];
            log.debug('DISPLAY', 'focus last:', feed.get('username'));
            INSTANCE.clearFocusUser(feed);
            feed.set('hasFocus', true);
          }
        };

        INSTANCE.autoFocusUser = function() {
          // In this case, don't autofocus, as the best focus code will focus the
          // most sensible person.
          if (INSTANCE.VideoFeeds.length < 3) {
            return;
          }
          var threshold = INSTANCE.configuration.audioSpeakingThreshold;
          var highestVol = threshold;
          var focusReducer = function(currFeed, nextFeed) {
            var vol = nextFeed.get('speakingVolume');
            if (vol > threshold) {
              if (vol > highestVol) {
                highestVol = vol;
                return nextFeed;
              }
              else {
                return currFeed;
              }
            }
            else {
              return currFeed;
            }
          }
          var focusUser = INSTANCE.VideoFeeds.reduce(focusReducer, null);
          var isLocalUser = _.isObject(focusUser) && (focusUser.id === INSTANCE.settings.userId);
          if (focusUser && !isLocalUser) {
            log.debug('AUTO FOCUS', 'focus user candidate', focusUser.get('username'));
            INSTANCE.clearFocusUser(focusUser);
            focusUser.set('hasFocus', true);
          }
        }

        INSTANCE.audioFeedRemoved = function(feed) {
          log.debug('FEED', 'audio feed removed', feed.get('username'));
          var color = feed.get('chatColor');
          if (color) {
            APP.Util.returnColor(INSTANCE._instance, color);
          }
        },

        INSTANCE.videoFeedAdded = function(feed) {
          log.debug('FEED', 'video feed added', feed.get('username'));
          INSTANCE.setBestFocus();
        },

        INSTANCE.videoFeedRemoved = function(feed) {
          log.debug('FEED', 'video feed removed', feed.get('username'));
          INSTANCE.setBestFocus();
        },

        INSTANCE.setVolumeToSilence = function() {
          log.debug('AUDIO', 'setting local audio volume to silent');
          INSTANCE.setLocalVolume(-100);
        }

        INSTANCE.isSpeaking = function(volume) {
          return !INSTANCE.AppState.get('localAudioMuted') && volume > INSTANCE.configuration.audioSpeakingThreshold;
        }

        INSTANCE.setFeedVolume = function(userId, volume) {
          var audioFeed = INSTANCE.AudioFeeds.get(userId);
          var videoFeed = INSTANCE.VideoFeeds.get(userId);
          if (audioFeed || videoFeed) {
            var speaking = INSTANCE.isSpeaking(volume);
            var attrs = {
              speaking: speaking,
              speakingVolume: volume,
            }
            audioFeed && audioFeed.set(attrs);
            videoFeed && videoFeed.set(attrs);
            if (audioFeed && INSTANCE.settings.userId === INSTANCE.configuration.verboseDebuggingUserId) {
              log.trace('FEED VOLUME', audioFeed.get('username') + ':', volume, speaking);
            }
          }
        }

        INSTANCE.setLocalVolume = function(volume) {
          INSTANCE.sendVolumeData(volume);
        }

        INSTANCE.handleVolumeEvents = function(volume) {
          var speaking = INSTANCE.isSpeaking(volume);
          if (speaking) {
            INSTANCE.volumeEventsEnabled = true;
          }
          else if (!speaking && INSTANCE.volumeEventsEnabled) {
            INSTANCE.volumeEventsEnabled = false;
            INSTANCE.setVolumeToSilence();
          }
          if (INSTANCE.volumeEventsEnabled) {
            INSTANCE.setLocalVolume(volume);
          }
          var autoFocus = INSTANCE.AppState.get('autoFocus');
          var view = INSTANCE.AppState.get('view');
          if (view == 'focus' && autoFocus) {
            INSTANCE.autoFocusUser();
          }
        };

        INSTANCE.sendVolumeData = function(volume) {
          var data = {
            userId: INSTANCE.settings.userId,
            volume: volume,
          }
          log.trace('FEED VOLUME', 'broadcasting', volume);
          INSTANCE.RoomSocket.publish('/' + INSTANCE.settings.roomName + '/volume', data);
        };

        INSTANCE.calculatePublishedFor = function(publishStreamGroups) {
          var publishingForGroups = {};
          for (var key in publishStreamGroups) {
            var streamGroup = publishStreamGroups[key];
            if (streamGroup.publishedFor) {
              for (var i in streamGroup.publishedFor) {
                publishingForGroups[streamGroup.publishedFor[i]] = true;
              }
            }
          }
          return publishingForGroups;
        }

        INSTANCE.audioMonitoring = function(stream) {
          INSTANCE.Audio.setup(stream, INSTANCE.configuration.audioPollingInterval, INSTANCE.handleVolumeEvents);
        }

        INSTANCE.buildSubscribeStreamOptions = function(attributes) {
          var streamSubscribeOptions;
          if (attributes.isAudioStream) {
            streamSubscribeOptions = {
              audio: true,
              data: false,
              video: false,
            };
          }
          else if (attributes.isVideoStream) {
            streamSubscribeOptions = {
              audio: false,
              data: false,
              video: true,
            };
          }
          return streamSubscribeOptions;
        }

        INSTANCE.subscribeToStream = function(attributes) {
          var streamGroup = attributes.streamGroup;
          var publishedFor = attributes.publishedFor;
          // This logic expresses these rules:
          // 1. Only subscribe to a stream group that's also being published to
          //    (exception for connected users not publishing any streams, these
          //    are view only).
          // 2. Don't subscribe to any stream we're publishing in the group.
          // 3. Don't subscribe to any streams from a group we're not
          //    subscribing to.
          var subscribeStreamGroup = INSTANCE.settings.subscribeStreamGroup || INSTANCE.configuration.streamGroupDefault;
          if (!_.isEmpty(INSTANCE.settings.publishStreamGroups) && !INSTANCE.publishingForGroups[subscribeStreamGroup]) {
            var message = "Configuration error: cannot subscribe to group '" + subscribeStreamGroup + "', no streams published to group";
            log.error('STREAM', message);
            alert(message);
            throw message;
          }
          if (attributes.userId == INSTANCE.settings.userId) {
            log.debug('STREAM', "skip subscribing to user's own stream:", attributes);
            return false;
          }
          else if (publishedFor && publishedFor.indexOf(subscribeStreamGroup) == -1) {
            log.debug('STREAM', 'skip subscribing to video stream not in', subscribeStreamGroup, 'published for', publishedFor);
            return false;
          }
          return true;
        }

        INSTANCE.addLocalOrRemoteAttributes = function(attrs, isLocal) {
          var data = {
            id: attrs.userId,
            elementId: APP.Util.buildStreamElementId(attrs.userId),
          };
          if (isLocal) {
            data.username = INSTANCE.settings.username;
            data.isAudioMuted = INSTANCE.configuration.audioMuteDefault;
            data.isVideoMuted = INSTANCE.configuration.videoMuteDefault;
            data.isLocal = true;
          }
          else {
            data.username = attrs.username;
            data.isAudioMuted = attrs.isAudioMuted;
            data.isVideoMuted = attrs.isVideoMuted;
          }
          return data;
        }

        INSTANCE.addAudioFeed = function(streamId, streamAttributes, streamAttachFunc) {
          var isLocal = streamAttributes.userId == INSTANCE.settings.userId;
          var model = {
            streamAttachFunc: streamAttachFunc,
          };
          if (!isLocal) {
            model.audioStreamId = streamId;
          }
          var modelData = INSTANCE.addLocalOrRemoteAttributes(streamAttributes, isLocal);
          _.extend(model, modelData);
          var color;
          if (color = APP.Util.getColor(INSTANCE._instance)) {
            model.chatColor = color;
          }
          INSTANCE.AudioFeeds.add(model);
          log.debug("FEED", "adding audio feed", model);
        };

        INSTANCE.addVideoFeed = function(streamId, streamAttributes, streamAttachFunc) {
          var isLocal = streamAttributes.userId == INSTANCE.settings.userId;
          var tileSize = INSTANCE.getTileVideoFeedSize();
          var focusSize = INSTANCE.getFocusVideoFeedSize();
          var filmstripWidth = INSTANCE.getFilmstripVideoFeedWidth();
          var model = {
            streamAttachFunc: streamAttachFunc,
            videoStreamId: streamId,
            tileSize: tileSize,
            focusSize: focusSize,
            filmstripWidth: filmstripWidth,
          };
          var modelData = INSTANCE.addLocalOrRemoteAttributes(streamAttributes, isLocal);
          _.extend(model, modelData);
          INSTANCE.VideoFeeds.add(model);
          log.debug("FEED", "adding video feed", model);
          INSTANCE.resizeLayout();
        };

        INSTANCE.removeUserFeeds = function(userId) {
          var audioFeed = INSTANCE.AudioFeeds.get(userId);
          if (audioFeed) {
            log.debug("FEED", "removing audio feed", userId);
            audioFeed.trigger('destroy', audioFeed);
          }
          var videoFeed = INSTANCE.VideoFeeds.get(userId);
          if (videoFeed) {
            log.debug("FEED", "removing video feed", userId);
            videoFeed.trigger('destroy', videoFeed);
            INSTANCE.resizeLayout();
          }
        }

        INSTANCE.getDisplaySize = function(absolute) {
          var chatWidth = (!absolute && INSTANCE.AppState.get('chat')) ? INSTANCE.configuration.chatWidth : 0;
          var height;
          var width;
          if (INSTANCE.settings.displayContainer) {
            // Must reselect in case it's been dynamically added.
            var container = $(INSTANCE.settings.displayContainer);
            height = container.height();
            width = container.width();
            if (!INSTANCE.settings.videoOnlyDisplay) {
              height -= INSTANCE.getHeaderHeight();
              width -= chatWidth;
            }
          }
          else {
            height = window.innerHeight - INSTANCE.getHeaderHeight();
            width = window.innerWidth - chatWidth;
          }
          var size = {
            height: height,
            width: width,
            area: height * width,
          }
          log.trace("DISPLAY", "display size", size);
          return size;
        }

        INSTANCE.calculateTileSize = function(tiles, area, tileAreaPercentage) {
          var feedArea = area * tileAreaPercentage / 100 / tiles;
          var sqrt = Math.sqrt(feedArea / INSTANCE.configuration.horizontalDisplayRatio / INSTANCE.configuration.verticalDisplayRatio);
          var feedHeight = Math.floor((sqrt * INSTANCE.configuration.verticalDisplayRatio) - (INSTANCE.configuration.feedTileMargin / 2));
          var feedWidth = Math.floor((sqrt * INSTANCE.configuration.horizontalDisplayRatio) - (INSTANCE.configuration.feedTileMargin / 2));
          var size = {
            height: feedHeight,
            width: feedWidth,
          };
          log.trace("DISPLAY", "calculated tile size", size);
          return size;
        }

        INSTANCE.testMedianTileAreaPercentage = function(tiles, size, tileAreaPercentage) {
          var tileSize = INSTANCE.calculateTileSize(tiles, size.area, tileAreaPercentage);

          var hTiles = Math.floor(size.width / tileSize.width);
          var vTiles = Math.floor(size.height / tileSize.height);
          var fittedTiles = hTiles * vTiles;

          if (fittedTiles < tiles) {
            return false;
          }
          else {
            return tileSize;
          }
        }

        INSTANCE.getTileVideoFeedSize = function() {
          var tiles = INSTANCE.VideoFeeds.length;
          var size = INSTANCE.getDisplaySize();
          // It's possible for there to be zero tiles, or for the display area to
          // be zero-sized, and all of the subsequent math depends on having at
          // least one tile and a vaild display area, so bail here in this case.
          if (tiles < 1 || size.height < 1 || size.width < 1) {
            return {
              height: 0,
              width: 0,
            };
          }
          var reducer = function(successTileAreaPercentage, failTileAreaPercentage, tileSize) {
            // No need for perfection, this is close enough, and is achieved by
            // about 8 iterations.
            if (Math.abs(successTileAreaPercentage - failTileAreaPercentage) < 1) {
              // Theoretically, the calculated size should fit into the browser
              // window, but in practice sometimes it's a hair too big, the shrink
              // multiplier is meant to compensate.
              var shrink = .98;
              var finalSize = {
                height: Math.floor(tileSize.height * shrink),
                width: Math.floor(tileSize.width * shrink),
              };
              log.trace("DISPLAY", "final calculated tile size", finalSize);
              return finalSize;
            }
            else {
              var median = (successTileAreaPercentage + failTileAreaPercentage) / 2;
              var medianResult = INSTANCE.testMedianTileAreaPercentage(tiles, size, median);
              if (medianResult) {
                return reducer(median, failTileAreaPercentage, medianResult);
              }
              else {
                return reducer(successTileAreaPercentage, median, tileSize);
              }
            }
          }
          return reducer(0, 100);
        }

        INSTANCE.getFocusVideoFeedSize = function() {
          var size = INSTANCE.getDisplaySize(true);
          if (INSTANCE.AppState.get('chat') || INSTANCE.AppState.get('focusCrop')) {
            // TODO: No idea why the last addition is needed.
            var height = size.height - INSTANCE.getHeaderHeight() - INSTANCE.configuration.filmstripHeight + 30;
            var width = Math.floor(height * INSTANCE.configuration.horizontalDisplayRatio / INSTANCE.configuration.verticalDisplayRatio);
            var focusSize = {
              height: height,
              width: width,
            }
            log.trace('DISPLAY', 'focus size', focusSize);
            return focusSize;
          }
          else {
            return size;
          }
        }

        INSTANCE.getFilmstripVideoFeedWidth = function() {
          var size = INSTANCE.getDisplaySize(true);
          var count = INSTANCE.VideoFeeds.length;
          var totalWidth = (INSTANCE.configuration.filmstripSize.width * count) + (INSTANCE.configuration.filmstripMargin * count * 2);
          var width;
          if ((totalWidth + (INSTANCE.configuration.filmstripMargin * 4)) > size.width) {
            width = INSTANCE.configuration.filmstripSize.height;
          }
          else {
            width = INSTANCE.configuration.filmstripSize.width;
          }
          log.trace('DISPLAY', 'filmstrip width', width);
          return width;
        }

        APP.reqres.setHandler(INSTANCE._instance + ":feed:width", function() {
          return INSTANCE.getFilmstripVideoFeedWidth();
        });

        INSTANCE.getChatSize = function() {
          var size = INSTANCE.getDisplaySize(true);
          var view = INSTANCE.AppState.get('view');
          var margin = view == 'tile' ? 0 : INSTANCE.configuration.filmstripHeight;
          var props = {
            chatHeight: size.height - margin - (INSTANCE.configuration.chatMargin * 2),
          }
          return props;
        }

        INSTANCE.resizeLayout = function() {
          var displaySize = INSTANCE.getDisplaySize();
          var absDisplaySize = INSTANCE.getDisplaySize(true);
          var tileSize = INSTANCE.getTileVideoFeedSize();
          var focusSize = INSTANCE.getFocusVideoFeedSize();
          var filmstripWidth = INSTANCE.getFilmstripVideoFeedWidth();
          var isChatOpen = INSTANCE.AppState.get('chat');
          var isFocusCropped = INSTANCE.AppState.get('focusCrop');
          var attrs = {
            tileSize: tileSize,
            focusSize: focusSize,
            filmstripWidth: filmstripWidth,
          }
          INSTANCE.VideoFeeds.each(function(feed) {
            feed.set(attrs);
          });
          var chatProps = INSTANCE.getChatSize();
          var focusAttrs = {};
          var chatAttrs = {
            height: chatProps.chatHeight,
          };
          var filmstripAttrs = {};
          switch (INSTANCE.configuration.filmstripPlacement) {
            case 'top':
              filmstripAttrs.top = 0;
              break;
            default:
              filmstripAttrs.bottom = 0;
              break;
          }
          if (isChatOpen || isFocusCropped) {
            var chatWidthAdjustment = isChatOpen ? INSTANCE.configuration.chatWidth : 0;
            var marginSize = (absDisplaySize.width - chatWidthAdjustment - focusSize.width) / 2;
            focusAttrs['margin-left'] = marginSize;
            focusAttrs['margin-right'] = marginSize;
            var paddingTop;
            switch (INSTANCE.configuration.filmstripPlacement) {
              case 'top':
                paddingTop = INSTANCE.configuration.filmstripHeight;
                paddingBottom = 0;
                break;
              default:
                paddingTop = 0;
                paddingBottom = INSTANCE.configuration.filmstripHeight;
                break;
            }
            focusAttrs['padding-top'] = paddingTop;
            focusAttrs['padding-bottom'] = paddingBottom;

            if (INSTANCE.AppState.get('chat')) {
              var view = INSTANCE.AppState.get('view');
              if (view == 'tile') {
                chatAttrs['margin-top'] = INSTANCE.configuration.feedTileMargin;
              }
              else {
                switch (INSTANCE.configuration.filmstripPlacement) {
                  case 'top':
                    chatAttrs['margin-top'] = INSTANCE.configuration.filmstripHeight + INSTANCE.configuration.feedTileMargin;
                    break;
                  default:
                    chatAttrs['margin-top'] = INSTANCE.configuration.feedTileMargin;
                    break;
                }
              }
            }
          }
          else {
            focusAttrs['margin-left'] = 'auto';
            focusAttrs['margin-right'] = 'auto';
            focusAttrs['padding-top'] = 0;
          }
          var attrs = {
            filmstripAttrs: filmstripAttrs,
            focusCssAttrs: focusAttrs,
            chatCssAttrs: chatAttrs,
            displaySize: displaySize,
          }
          INSTANCE.AppState.set('layout', attrs);
          // TODO: This should be handled by the Chat view.
          if (INSTANCE.ChatInstance) {
            INSTANCE.ChatInstance.scrollMessages();
          }
        };
        return INSTANCE;
      }

    });
  }

  return ControllerFactory;

});

Home

Modules

App

Controller

Classes

RoomAppFactory

makeInstance

PluginRoom

Namespaces

RoomPluginInstance

    Documentation generated by JSDoc 3.4.3 on Sun Feb 12 2017 16:32:42 GMT-0800 (PST)


text:
===============
Cost Management
===============

This section details a few use cases and their approximate costs in order to help users estimate cloud costs for their analyses.

Estimating Costs for Common Bioinformatics and Data Analysis Tasks
==================================================================

The following table summarizes order-of-magnitude costs for common data analysis tasks. For example an order-of-magnitude cost of $10 indicates that the cost can be up to $10, estimated from the given example notebooks. Estimated costs between $10 and $100 are reported as the next order of magnitude, $100:

.. list-table::
   :widths: 100 25 25 25
   :align: center
   :header-rows: 1

   * - Bioinformatics / Data Analysis Task
     - Dataset(s)
     - Tools
     - Approx. Cost (Max)
   * - | Identify differentially expressed genes
       | `Example: Breast Cancer Tumor vs. Normal <https://github.com/isb-cgc/Community-Notebooks/blob/master/Notebooks/How_to_analyze_differential_expression_between_paired_tumor_and_normal_samples.ipynb>`_
     - TCGA
     - BigQuery, Colab, Python, R
     - $1
   * - | Train a prediction model using gene expression data
       | `Example: Logistic Regression, Ovarian Cancer Chemo Response <https://github.com/isb-cgc/Community-Notebooks/blob/master/MachineLearning/How_to_build_an_RNAseq_logistic_regression_classifier_with_BigQuery_ML.ipynb>`_
       | `Example: Logistic Regression, Breast Cancer Tumor vs. Normal <https://github.com/isb-cgc/Community-Notebooks/blob/master/TeachingMaterials/2021-10-NIHLibrarySession/BigQueryMachineLearning.ipynb>`_
     - TCGA
     - BigQuery, BigQuery ML, Colab, Python, R
     - $1 ($100) \*
   * - | Train a linear regression model using gene expression data
       | `Example: Linear Regression, Kidney Cancer Survival <https://github.com/isb-cgc/Community-Notebooks/blob/master/MachineLearning/How_to_predict_cancer_survival_with_BigQueryML.ipynb>`_
     - TCGA
     - BigQuery, BigQuery ML, Colab, Python
     - $1 ($100) \*
   * - | Train a deep neural network (DNN) regression model using gene expression data
       | `Example: Regression w/ TensorFlow, Kidney Cancer Survival <https://github.com/isb-cgc/Community-Notebooks/blob/master/MachineLearning/How_to_predict_cancer_survival_with_TensorFlow.ipynb>`_
     - TCGA
     - BigQuery, Colab, TensorFlow, Compute Engine w/ GPUs
     - $1 \*\*
   * - | Analyze RNA-seq data using the GDC workflow
       | `Example: GDC RNA-seq CWL Workflow <https://github.com/NCI-GDC/gdc-rnaseq-cwl>`_
     - TCGA
     - Compute Engine, Cloud Storage, CWL
     - $10 \*\*\*

* \*BigQuery ML costs depend on data size. In these examples, a subset of data was extracted to a temporary table, which was used as input to BigQuery ML. This reduces costs substantially. If using all gene features of a TCGA dataset, costs can grow to the order of $100.

* \*\*With small datasets, use of GPUs in Colab does not cost extra (unless using `Colab Pro <https://research.google.com/colaboratory/faq.html>`_). However, if TensorFlow code is executed in a VM with GPUs, the hourly cost can range from $1 to $10.

* \*\*\*Cost per sample depends on sample size (i.e., number of reads) and processing time.

* BigQuery ML vs. TensorFlow w/ Compute Engine or Colab GPUs: When choosing between these tools for machine learning, consider the following guidelines:

  - TensorFlow w/ Compute Engine or Colab GPUs: Appropriate for data exploration or parameter tuning requiring multiple iterations of training and evaluation.

  - BigQuery ML: Appropriate for production deployment of machine learning models. For example, after optimizing model parameters, train and deploy the final model with BigQuery ML.

redpajama_set_name: RedPajamaGithub


text:
layout: post
title: Some Slides about Containers
date: 2017-06-08 17:09
categories: ['Cloud Computing']
tags: ['Container', 'Slides']
author: Yanxi Chen
mathjax: true
---

* content
{:toc}

[Anchor-The Rapid Rise of Containers in Production]({{site.url}}/assets/Some-Slides-about-Containers-UCSB-nurmi.pdf)

[Kubernetes]({{site.url}}/assets/Some-Slides-about-Containers-Kubernetes.pdf)

<!--more-->


text:
package net.opengis.gml.impl;
/**
 * An XML CountExtentType(@http://www.opengis.net/gml).
 *
 * This is a list type whose items are net.opengis.gml.IntegerOrNull.
 */
public class CountExtentTypeImpl extends org.apache.xmlbeans.impl.values.XmlListImpl implements net.opengis.gml.CountExtentType
{
    private static final long serialVersionUID = 1L;

    public CountExtentTypeImpl(org.apache.xmlbeans.SchemaType sType)
    {
        super(sType, false);
    }

    protected CountExtentTypeImpl(org.apache.xmlbeans.SchemaType sType, boolean b)
    {
        super(sType, b);
    }
}


text:
<?php

namespace Phine\Bundles\BuiltIn;
use Phine\Bundles\Core;
use Phine\Bundles\Core\Logic\Bundle\BundleManifest;
use Phine\Bundles\Core\Logic\Bundle\BundleManufacturer;
use Phine\Bundles\Core\Logic\Bundle\BundleDependency;
use Phine\Framework\System\IO\Path;

class Manifest extends BundleManifest
{
    /**
     *
     * @var Core\Manifest;
     */
    private $core;

    /**
     * Creates the BuiltIn bundle manifest
     */
    function __construct()
    {
        $this->core = new Core\Manifest();
    }

    /**
     * Bundle version
     * @return string Returns the bundle version
     */
    public function Version()
    {
        return '1.0.4';
    }
    /**
     * Loads extra code not available by autoload
     */
    protected function LoadBackendCode()
    {
        require_once Path::Combine(__DIR__, 'Globals/AddHooks.php');
    }
    /**
     *
     * @return BundleManufacturer
     */
    public function Manufacturer()
    {
        return $this->core->Manufacturer();
    }

    /**
     * The dependencies of the bundle
     * @return BundleDependency[]
     */
    public function Dependencies()
    {
        return array(new BundleDependency('Core', '1.2.3', '1.2.3'));
    }

    /*
     * Loads code not coverable by phine autoload
     */
    protected function LoadFrontendCode()
    {
        //require_once Path::Combine(__DIR__, 'ThirdParty/PHPMailer/PHPMailerAutoload.php');
    }

}


text:
/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.modeshape.web.client.contents;

import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import org.modeshape.web.shared.ModalDialog;

/**
 * Dialog asking node's name and primary type and creating new node.
 *
 * @author kulikov
 */
public class AddNodeDialog extends ModalDialog {

    private TextItem name = new TextItem();
    private ComboBoxItem primaryType = new ComboBoxItem();
    private Contents contents;

    public AddNodeDialog(Contents contents) {
        super("Create new node", 450, 250);
        this.contents = contents;

        name.setName("name");
        name.setTitle("Node name");
        name.setDefaultValue("");
        name.setWidth(250);
        name.setRequired(true);
        name.setVisible(true);
        name.setStartRow(true);
        name.setEndRow(true);

        primaryType.setName("primaryType");
        primaryType.setTitle("Primary Type");
        primaryType.setDefaultValue("nt:unstructured");
        primaryType.setWidth(250);
        primaryType.setRequired(true);
        primaryType.setStartRow(true);
        primaryType.setEndRow(true);

        StaticTextItem description = new StaticTextItem();
        description.setValue("Please specify the name of node and choose type");
        description.setTitle("");
        description.setStartRow(true);
        description.setEndRow(true);

        setControls(description, name, primaryType);
    }

    public void setPrimaryTypes(String[] primaryTypes) {
        primaryType.setValueMap(primaryTypes);
    }

    @Override
    public void onConfirm(com.smartgwt.client.widgets.form.fields.events.ClickEvent event) {
        contents.addNode(name.getValueAsString(), primaryType.getValueAsString());
    }
}


text:
Ruby app that sends and receives messages to a space in
[IBM Watson Workspace] (https://workspace.ibm.com).

The Watson Work platform provides **spaces** for people to exchange
**messages** in conversations. This sample app shows how to retrieve
information about a space, then send a message to the conversation in
that space. It also demonstrates how to authenticate an application and
obtain the OAuth token needed to make Watson Work API calls.

### Registering the app

In your Web browser, go to [Watson Work Services - Apps]
(https://workspace.ibm.com/developer/apps), add a new app named
**Hello World** and write down its app id and app secret.

### Adding the app to a space

Leave the terminal window open as you'll need it again soon.

In your Web browser, go to [Watson Workspace](https://workspace.ibm.com),
create a space named **Examples**, then open the **Apps** tab for that space
and add the **Hello World** app to it.

## What API does the app use?

The app uses the [Watson Work OAuth API]
(https://workspace.ibm.com/developer/docs) to authenticate and get an
OAuth token.  It then uses the [Watson Work GraphQL API]
(https://workspace.ibm.com/developer/docs) to retrieve a list of spaces.
Finally, it uses the [Watson Work Spaces API]
(https://workspace.ibm.com/developer/docs) to send a message to the
conversation in the selected space.

## How can I contribute?

Pull requests welcome!


text:
set -e
set -u

source tests/scripts/setup-pytest-env.sh

# Rebuild cython
make cython3

# cleanup pycache
find . -type f -path "*.pyc" | xargs rm -f

run_pytest cython python-topi-nightly tests/python/topi/nightly


text:
package awsup

import (
	"fmt"
	"os"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/golang/glog"
	"k8s.io/kops/pkg/apis/kops"
)

// allRegions is the list of all regions; tests will set the values
var allRegions []*ec2.Region
var allRegionsMutex sync.Mutex

// isRegionCompiledInToAWSSDK checks if the specified region is in the AWS SDK
func isRegionCompiledInToAWSSDK(region string) bool {
	resolver := endpoints.DefaultResolver()
	partitions := resolver.(endpoints.EnumPartitions).Partitions()
	for _, p := range partitions {
		for _, r := range p.Regions() {
			if r.ID() == region {
				return true
			}
		}
	}
	return false
}

// ValidateRegion checks that an AWS region name is valid
func ValidateRegion(region string) error {
	if isRegionCompiledInToAWSSDK(region) {
		return nil
	}

	allRegionsMutex.Lock()
	defer allRegionsMutex.Unlock()

	if allRegions == nil {
		glog.V(2).Infof("Querying EC2 for all valid regions")

		request := &ec2.DescribeRegionsInput{}
		awsRegion := os.Getenv("AWS_REGION")
		if awsRegion == "" {
			awsRegion = "us-east-1"
		}
		config := aws.NewConfig().WithRegion(awsRegion)
		config = config.WithCredentialsChainVerboseErrors(true)

		sess, err := session.NewSession(config)
		if err != nil {
			return fmt.Errorf("Error starting a new AWS session: %v", err)
		}

		client := ec2.New(sess, config)

		response, err := client.DescribeRegions(request)
		if err != nil {
			return fmt.Errorf("Got an error while querying for valid regions (verify your AWS credentials?): %v", err)
		}
		allRegions = response.Regions
	}

	for _, r := range allRegions {
		name := aws.StringValue(r.RegionName)
		if name == region {
			return nil
		}
	}

	if os.Getenv("SKIP_REGION_CHECK") != "" {
		glog.Infof("AWS region does not appear to be valid, but skipping because SKIP_REGION_CHECK is set")
		return nil
	}

	return fmt.Errorf("Region is not a recognized EC2 region: %q (check you have specified valid zones?)", region)
}

// FindRegion determines the region from the zones specified in the cluster
func FindRegion(cluster *kops.Cluster) (string, error) {
	region := ""

	nodeZones := make(map[string]bool)
	for _, subnet := range cluster.Spec.Subnets {
		if len(subnet.Zone) <= 2 {
			return "", fmt.Errorf("invalid AWS zone: %q in subnet %q", subnet.Zone, subnet.Name)
		}

		nodeZones[subnet.Zone] = true

		zoneRegion := subnet.Zone[:len(subnet.Zone)-1]
		if region != "" && zoneRegion != region {
			return "", fmt.Errorf("Clusters cannot span multiple regions (found zone %q, but region is %q)", subnet.Zone, region)
		}

		region = zoneRegion
	}

	return region, nil
}

// FindEC2Tag find the value of the tag with the specified key
func FindEC2Tag(tags []*ec2.Tag, key string) (string, bool) {
	for _, tag := range tags {
		if key == aws.StringValue(tag.Key) {
			return aws.StringValue(tag.Value), true
		}
	}
	return "", false
}

// FindASGTag find the value of the tag with the specified key
func FindASGTag(tags []*autoscaling.TagDescription, key string) (string, bool) {
	for _, tag := range tags {
		if key == aws.StringValue(tag.Key) {
			return aws.StringValue(tag.Value), true
		}
	}
	return "", false
}

// FindELBTag find the value of the tag with the specified key
func FindELBTag(tags []*elb.Tag, key string) (string, bool) {
	for _, tag := range tags {
		if key == aws.StringValue(tag.Key) {
			return aws.StringValue(tag.Value), true
		}
	}
	return "", false
}

// AWSErrorCode returns the aws error code, if it is an awserr.Error, otherwise ""
func AWSErrorCode(err error) string {
	if awsError, ok := err.(awserr.Error); ok {
		return awsError.Code()
	}
	return ""
}

// AWSErrorMessage returns the aws error message, if it is an awserr.Error, otherwise ""
func AWSErrorMessage(err error) string {
	if awsError, ok := err.(awserr.Error); ok {
		return awsError.Message()
	}
	return ""
}


text:
'use strict';

// MODULES //

var addon = require( './../src/addon.node' );

// MAIN //

/**
* Computes the standard error of the mean for a double-precision floating-point strided array using a one-pass textbook algorithm.
*
* @param {PositiveInteger} N - number of indexed elements
* @param {number} correction - degrees of freedom adjustment
* @param {Float64Array} x - input array
* @param {integer} stride - stride length
* @returns {number} standard error of the mean
*
* @example
* var Float64Array = require( '@stdlib/array/float64' );
*
* var x = new Float64Array( [ 1.0, -2.0, 2.0 ] );
* var N = x.length;
*
* var v = dsemtk( N, 1, x, 1 );
* // returns ~1.20185
*/
function dsemtk( N, correction, x, stride ) {
	return addon( N, correction, x, stride );
}

// EXPORTS //

module.exports = dsemtk;


text:
body{background-color:#272B2E !important;color:#999 !important;}
div::-webkit-scrollbar {width: 1em;}
div::-webkit-scrollbar-thumb {background-color: #535353;}
a{color:red !important;font-weight:bold;}
h1 {color:#5F5F5F;text-transform:uppercase;}
h2, h3 {color:white;text-shadow:1px 1px black;}
.clearfix:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;}
.gwt-Hyperlink{display:inline-block;}
.gwt-DisclosurePanel {width: 100%;padding: 0.5em;border-radius: 0.4em;color: #BDBDBD;}
.gwt-DisclosurePanel .header, .gwt-DisclosurePanel .header a, .gwt-DisclosurePanel .header td {color:#A7A7A7 !important;}
.gwt-RichTextArea {background-color:white;width:100%;}
.gwt-PopupPanel{border-color:black !important;border-radius:0.5em;box-shadow:0 0 2em white;background-color:black !important}
.gwt-Button {
	padding: 1em !important;
	background: #141414 !important;
	color: white;
	border: 1px solid #0F0F0F !important;
	border-radius: 0.3em !important;
}

.gwt-Button:hover {
	box-shadow: 0 0 0.5em #A7A7A7;
	border-color: black;
}


text:
class CreateTimeSlots < ActiveRecord::Migration
  def self.up
    create_table :time_slots do |t|
      t.references :location
      t.references :calendar
      t.references :repeating_event
      t.datetime :start
      t.datetime :end
      t.boolean :active
      t.timestamps
    end
  end

  def self.down
    drop_table :time_slots
  end
end


text:
@interface PodsDummy_Nimbus_Core_Models : NSObject
@end
@implementation PodsDummy_Nimbus_Core_Models
@end


text:
[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [discardHashTag](./browser-tracker.discardhashtag.md)

## discardHashTag() function

Strip hash tag (or anchor) from URL

Signature:

```typescript
declare function discardHashTag(enable: boolean, trackers?: Array<string>): void;
```

## Parameters

|  Parameter | Type | Description |
|  --- | --- | --- |
|  enable | boolean | Whether to enable stripping of hash |
|  trackers | Array<string> | The tracker identifiers which will be configured |

Returns:

void


text:
UIDeviceHardware
================

A simple and easy to use helper class to extract more precisely the current hardware platform being used by the user.

It outputs three formats:

1. Platform format: The raw platform given by the system:

		// Outputs something like: iPhone3,1, iPod2,1, iPad3,6
        [UIDeviceHardware platform]

2. PlarformString format: A friendly and more readable format:

        // Outputs something like: iPhone 4, iPod Touch 2G, iPad 4 (GSM+CDMA)
        [UIDeviceHardware platformString]

3. GenericDeviceType format: A smart output based on the device type and screen size

        // Outputs something like: UIDeviceHardwareGenericTypePhone35, UIDeviceHardwareGenericTypePod40
        [UIDeviceHardware genericDeviceType]

General considerations
-------------------------
* Uses a more low level system information extractor for better performance
* This will be a constantly revised source for new devices released by Apple (last update: 12/30/2013)
* Supports even the simulator if needed (x86_64/Simulator/UIDeviceHardwareGenericTypeUnknown)

Live examples
-------------------------

    if ([UIDeviceHardware genericDeviceType] == UIDeviceHardwareGenericTypePhone35 ||
    	 [UIDeviceHardware genericDeviceType] == UIDeviceHardwareGenericTypePod35){
    	// do some stuff targeted to 3,5" iPhone or iPod Touch screens
    }
    else if ([UIDeviceHardware genericDeviceType] == UIDeviceHardwareGenericTypePhone40 ||
    	 [UIDeviceHardware genericDeviceType] == UIDeviceHardwareGenericTypePod40){
    	// do some stuff targeted to 4.0" iPhone or iPod Touch screens
    }
    else if ([UIDeviceHardware genericDeviceType] == UIDeviceHardwareGenericTypePad97){
    	// do some stuff targeted to the bigger 9,7" iPads
    }
    else if ([UIDeviceHardware genericDeviceType] == UIDeviceHardwareGenericTypePad79){
    	// do some stuff targeted to the iPad Minis
    }
    else{
    	// unknown hardware type.
    	// Possibly the simulator. Should throw an exception
    }


text:
<?php
/**
 * ItemType.php
 *
 * @since 23/08/14
 * @author Gerhard Seidel <gseidel.message@googlemail.com>
 */

namespace Enhavo\Bundle\GridBundle\Form\Type;

use Enhavo\Bundle\GridBundle\Item\ItemTypeResolver;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Enhavo\Bundle\GridBundle\Entity\Item;

class ItemType extends AbstractType
{
    /**
     * @var ItemTypeResolver
     */
    protected $resolver;

    public function __construct(ItemTypeResolver $itemTypeResolver)
    {
        $this->resolver = $itemTypeResolver;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('order', 'hidden', array(
            'data' => 0
        ));

        $builder->add('type', 'hidden');

        $resolver = $this->resolver;
        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($resolver){
            $item = $event->getData();
            $form = $event->getForm();
            if(!empty($item) && isset($item['type'])) {
                $form->add('itemType', $resolver->getFormType($item['type']));
            }
        });

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($resolver){
            $item = $event->getData();
            $form = $event->getForm();
            if(!empty($item) && $item->getType()) {
                $form->add('itemType', $resolver->getFormType($item->getType()));
            }
        });
    }

    /**
     * {@inheritdoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $data = $form->getData();
        if($data instanceof Item) {
            $view->vars['label'] = $this->resolver->getLabel($data->getType());
        }

        return;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Enhavo\Bundle\GridBundle\Entity\Item'
        ));
    }

    public function getName()
    {
        return 'enhavo_grid_item';
    }
}


text:
You can use the [editor on GitHub](https://github.com/IanDrummond/web/edit/master/README.md) to maintain and preview the content for your website in Markdown files.

Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.

### Markdown

Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for

```markdown
Syntax highlighted code block

# Header 1
## Header 2
### Header 3

*list*

- Bulleted
- List

1. Numbered
2. List

**Bold** and _Italic_ and `Code` text

[Link](url) and ![Image](src)
```

For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).

### Jekyll Themes

Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/IanDrummond/web/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.

### Support or Contact

Having trouble with Pages? Check out our [documentation](https://help.github.com/categories/github-pages-basics/) or [contact support](https://github.com/contact) and we'll help you sort it out.

You can use the [editor on GitHub](https://github.com/IanDrummond/web/edit/master/README.md) to maintain and preview the content for your website in Markdown files.

###Contact Information

p: +1.440.364.2707

e: iandrummond@me.com

[Facebook](https://www.facebook.com/iancolindrummond)

[Twitter](https://twitter.com/iandrummond)

[Instagram](https://www.instagram.com/iandrummond/)


text:
package womtool.wom2wdlom

import cats.syntax.traverse._
import cats.instances.list._
import cats.instances.either._
import common.Checked
import common.collections.EnhancedCollections._
import common.transforms.CheckedAtoB
import common.validation.Checked._
import shapeless.{Inl, Inr}
import wdl.draft2.model.command.{ParameterCommandPart, StringCommandPart}
import wdl.model.draft3.elements._
import wdl.model.draft3.elements.CommandPartElement.{PlaceholderCommandPartElement, StringCommandPartElement}
import wdl.model.draft3.elements.ExpressionElement.ExpressionLiteralElement
import wom.callable.Callable._
import wom.callable.Callable.OutputDefinition
import wom.callable.{CallableTaskDefinition, MetaValueElement, WorkflowDefinition}
import wom.executable.WomBundle
import wom.expression.WomExpression
import wom.graph._
import wom.graph.CallNode.InputDefinitionPointer
import wom.graph.expression._
import wom.graph.GraphNodePort.GraphNodeOutputPort
import wom.RuntimeAttributes
import wom.types._

object WomToWdlom {

  private def invalidFromString(text: String) =
    s"Value \'$text\' has no representation in the destination format (WDL)".invalidNelCheck

  def graphOutputNodeToWorkflowGraphElement: CheckedAtoB[GraphOutputNode, WorkflowGraphElement] =
    CheckedAtoB.fromCheck { a: GraphOutputNode => a match {
      case a: ExpressionBasedGraphOutputNode =>
        for {
          typeElement <- womTypeToTypeElement(a.womType)
          expression <- womExpressionToExpressionElement(a.womExpression)
        } yield {
          OutputDeclarationElement(
            typeElement,
            a.identifier.localName.value,
            expression)
        }
      case _ =>
        invalidFromString(a.toString)
      }
    }

  def womBundleToFileElement: CheckedAtoB[WomBundle, FileElement] =
    CheckedAtoB.fromCheck { a: WomBundle =>
      val tasks: Iterable[CallableTaskDefinition] = a.allCallables.values.filterByType[CallableTaskDefinition]
      val workflows: Iterable[WorkflowDefinition] = a.allCallables.values.filterByType[WorkflowDefinition]

      for {
        workflows <- workflows.map(workflowDefinitionToWorkflowDefinitionElement(_)).toList.sequence[Checked, WorkflowDefinitionElement]
        tasks <- tasks.map(callableTaskDefinitionToTaskDefinitionElement(_)).toList.sequence[Checked, TaskDefinitionElement]
      } yield {
        FileElement(
          Seq.empty, // Imports do not exist in WOM and cannot sensibly be added at this point
          Seq.empty, // Structs do not exist in draft-2
          workflows,
          tasks
        )
      }
    }

  def mapToMetaSectionElement: CheckedAtoB[Map[String, MetaValueElement], Option[MetaSectionElement]] =
    CheckedAtoB.fromCheck { a: Map[String, MetaValueElement] =>
      if (a.nonEmpty)
        Some(MetaSectionElement(a)).validNelCheck
      else
        None.validNelCheck
    }

  def mapToParameterMetaSectionElement: CheckedAtoB[Map[String, MetaValueElement], Option[ParameterMetaSectionElement]] =
    CheckedAtoB.fromCheck { a: Map[String, MetaValueElement] =>
      if (a.nonEmpty)
        Some(ParameterMetaSectionElement(a)).validNelCheck
      else
        None.validNelCheck
    }

  def runtimeAttributesToRuntimeAttributesSectionElement: CheckedAtoB[RuntimeAttributes, Option[RuntimeAttributesSectionElement]] =
    CheckedAtoB.fromCheck { a: RuntimeAttributes =>
      def tupleToKvPair(tuple: (String, WomExpression)): Checked[ExpressionElement.KvPair] = {
        for {
          expressionElement <- womExpressionToExpressionElement(tuple._2)
        } yield {
          ExpressionElement.KvPair(tuple._1, expressionElement)
        }
      }

      for {
        kvPairs <- (a.attributes map tupleToKvPair).toList.sequence[Checked, ExpressionElement.KvPair]
      } yield {
        if (kvPairs.nonEmpty)
          Some(RuntimeAttributesSectionElement(kvPairs.toVector))
        else
          None
      }
    }

  def outputDefinitionToOutputDeclarationElement: CheckedAtoB[OutputDefinition, OutputDeclarationElement] =
    CheckedAtoB.fromCheck { a: OutputDefinition =>
      for {
        typeElement <- womTypeToTypeElement(a.womType)
        expression <- womExpressionToExpressionElement(a.expression)
      } yield {
        OutputDeclarationElement(typeElement, a.name, expression)
      }
    }

  def inputDefinitionToInputDeclarationElement: CheckedAtoB[InputDefinition, InputDeclarationElement] =
    CheckedAtoB.fromCheck { a: InputDefinition =>
      womTypeToTypeElement(a.womType) flatMap { womType =>
        a match {
          case a: RequiredInputDefinition =>
            InputDeclarationElement(womType, a.localName.value, None).validNelCheck
          case a: OverridableInputDefinitionWithDefault =>
            womExpressionToExpressionElement(a.default) map { expression =>
              InputDeclarationElement(womType, a.localName.value, Some(expression))
            }
          case a: FixedInputDefinitionWithDefault =>
            womExpressionToExpressionElement(a.default) map { expression =>
              InputDeclarationElement(womType, a.localName.value, Some(expression))
            }
          case a: OptionalInputDefinition =>
            InputDeclarationElement(womType, a.localName.value, None).validNelCheck
        }
      }
    }

  def callableTaskDefinitionToTaskDefinitionElement: CheckedAtoB[CallableTaskDefinition, TaskDefinitionElement] =
    CheckedAtoB.fromCheck {
      a: CallableTaskDefinition =>
        val inputs: Checked[List[InputDeclarationElement]] =
          a.inputs.map(inputDefinitionToInputDeclarationElement(_)).sequence[Checked, InputDeclarationElement]
        val outputs: Checked[List[OutputDeclarationElement]] =
          a.outputs.map(outputDefinitionToOutputDeclarationElement(_)).sequence[Checked, OutputDeclarationElement]

        for {
          runtime <- runtimeAttributesToRuntimeAttributesSectionElement(a.runtimeAttributes)
          meta <- mapToMetaSectionElement(a.meta)
          parameterMeta <- mapToParameterMetaSectionElement(a.parameterMeta)
          inputs <- inputs
          outputs <- outputs
          commands <- a.commandTemplateBuilder(Map()).toEither
        } yield {
          val commandLine = CommandSectionLine(commands map {
            case s: StringCommandPart =>
              StringCommandPartElement(s.literal)
            case p: ParameterCommandPart =>
              val attrs = PlaceholderAttributeSet(
                defaultAttribute = p.attributes.get("default"),
                trueAttribute = p.attributes.get("true"),
                falseAttribute = p.attributes.get("false"),
                sepAttribute = p.attributes.get("sep")
              )

              PlaceholderCommandPartElement(ExpressionLiteralElement(p.expression.toWomString), attrs)
          })

          TaskDefinitionElement(
            a.name,
            if (inputs.nonEmpty) Some(InputsSectionElement(inputs)) else None,
            Seq.empty, // No such thing in draft-2
            if (outputs.nonEmpty) Some(OutputsSectionElement(outputs)) else None,
            CommandSectionElement(Seq(commandLine)),
            runtime,
            meta,
            parameterMeta,
            a.sourceLocation
          )
        }
      }

  def workflowDefinitionToWorkflowDefinitionElement: CheckedAtoB[WorkflowDefinition, WorkflowDefinitionElement] =
    CheckedAtoB.fromCheck { a: WorkflowDefinition =>
        // This is a bit odd, so let's explain. "Real" inputs/outputs that are specified by the WDL's author
        // cannot have periods in them - period. So any input/output that has a period in it
        // is an artifact of WOMification and should be dropped
        val inputs =
          a.inputs.filter(!_.localName.value.contains(".")).map(inputDefinitionToInputDeclarationElement(_)).sequence[Checked, InputDeclarationElement]
        val outputs =
          a.outputs.filter(!_.localName.value.contains(".")).map(outputDefinitionToOutputDeclarationElement(_)).sequence[Checked, OutputDeclarationElement]

        for {
          meta <- mapToMetaSectionElement(a.meta)
          parameterMeta <- mapToParameterMetaSectionElement(a.parameterMeta)
          inputs <- inputs
          outputs <- outputs
          nodes <- selectWdlomRepresentableNodes(a.graph.nodes).map(graphNodeToWorkflowGraphElement(_)).toList.sequence[Checked, WorkflowGraphElement]
        } yield {
          WorkflowDefinitionElement(
            a.name,
            if (inputs.nonEmpty) Some(InputsSectionElement(inputs)) else None,
            nodes.toSet,
            if (outputs.nonEmpty) Some(OutputsSectionElement(outputs)) else None,
            meta,
            parameterMeta,
            a.sourceLocation)
        }
      }

  def expressionNodeLikeToWorkflowGraphElement: CheckedAtoB[ExpressionNodeLike, WorkflowGraphElement] =
    CheckedAtoB.fromCheck {
      case a: ExpressionNode =>
        for {
          typeElement <- womTypeToTypeElement(a.womType)
          expression <- expressionNodeToExpressionElement(a)
        } yield {
          IntermediateValueDeclarationElement(
            typeElement = typeElement,
            name = a.identifier.localName.value,
            expression = expression)
        }
      case a: ExpressionCallNode => invalidFromString(a.toString)
    }

  // Select only nodes that have a Wdlom representation (i.e. were not synthesized during WOMification)
  // WOM has some explicit representations that are implicit in WDL; they are necessary for execution,
  // but do not make sense (or are illegal) in WDL source.
  private def selectWdlomRepresentableNodes(allNodes: Set[GraphNode]): Set[GraphNode] = {
    val expressions = allNodes.filterByType[ExposedExpressionNode]
    val scatters = allNodes.filterByType[ScatterNode]
    val calls = allNodes.filterByType[CallNode]
    val conditionals = allNodes.filterByType[ConditionalNode]

    expressions ++ scatters ++ calls ++ conditionals
  }

  def graphNodeToWorkflowGraphElement: CheckedAtoB[GraphNode, WorkflowGraphElement] =
    CheckedAtoB.fromCheck {
      case a: CallNode =>
        callNodeToCallElement(a)
      case a: ConditionalNode =>
        for {
          condition <- expressionNodeToExpressionElement(a.conditionExpression)
          nodes <- selectWdlomRepresentableNodes(a.innerGraph.nodes).toList.map(graphNodeToWorkflowGraphElement(_)).sequence[Checked, WorkflowGraphElement]
        } yield {
          IfElement(
            conditionExpression = condition,
            graphElements = nodes
          )
        }
      case a: ExpressionNodeLike =>
        expressionNodeLikeToWorkflowGraphElement(a)
      case a: GraphNodeWithSingleOutputPort =>
        graphNodeWithSingleOutputPortToWorkflowGraphElement(a)
      case a: GraphOutputNode =>
        graphOutputNodeToWorkflowGraphElement(a)
      case a: ScatterNode =>
        // Only CWL has multi-variable scatters
        if (a.scatterVariableNodes.size != 1)
          s"In WDL, the scatter node count must be exactly one, was ${a.scatterVariableNodes.size}".invalidNelCheck
        else
          for {
            expression <- expressionNodeToExpressionElement(a.scatterCollectionExpressionNodes.head)
            graph <- selectWdlomRepresentableNodes(a.innerGraph.nodes).toList.map(graphNodeToWorkflowGraphElement(_)).sequence[Checked, WorkflowGraphElement]
          } yield {
            ScatterElement(
              scatterName = a.identifier.localName.value,
              scatterExpression = expression,
              scatterVariableName = a.inputPorts.toList.head.name,
              graphElements = graph,
              sourceLocation = None
            )
          }
      }

  def graphNodeWithSingleOutputPortToWorkflowGraphElement: CheckedAtoB[GraphNodeWithSingleOutputPort, WorkflowGraphElement] =
    CheckedAtoB.fromCheck {
      case a: GraphInputNode =>
        womTypeToTypeElement(a.womType) map { typeElement =>
          InputDeclarationElement(
            typeElement,
            a.identifier.localName.value,
            None)
        }
      case a: ExpressionNode =>
        for {
          typeElement <- womTypeToTypeElement(a.womType)
          expression <- womExpressionToExpressionElement(a.womExpression)
        } yield {
          IntermediateValueDeclarationElement(
            typeElement,
            a.identifier.localName.value,
            expression)
        }
    }

  def womTypeToTypeElement: CheckedAtoB[WomType, TypeElement] =
    CheckedAtoB.fromCheck {
      case a: WomArrayType =>
        womTypeToTypeElement(a.memberType) map { typeElement =>
          if (a.guaranteedNonEmpty)
            NonEmptyTypeElement(ArrayTypeElement(typeElement))
          else
            ArrayTypeElement(typeElement)
        }
      case _: WomCoproductType => invalidFromString("WDL does not have coproducts - is this WOM from CWL?")
      case _: WomFileType => PrimitiveTypeElement(WomSingleFileType).validNelCheck
      case a: WomMapType =>
        for {
          keyType <- womTypeToTypeElement(a.keyType)
          valueType <- womTypeToTypeElement(a.valueType)
        } yield {
          MapTypeElement(keyType, valueType)
        }
      case _: WomNothingType.type => invalidFromString("WDL does not have the Nothing type - is this WOM from CWL?")
      case _: WomObjectType.type => ObjectTypeElement.validNelCheck
      case a: WomOptionalType => womOptionalTypeToOptionalTypeElement(a)
      case a: WomPairType =>
        for {
          leftType <- womTypeToTypeElement(a.leftType)
          rightType <- womTypeToTypeElement(a.rightType)
        } yield {
          PairTypeElement(leftType, rightType)
        }
      case a: WomPrimitiveType => womPrimitiveTypeToPrimitiveTypeElement(a)
    }

  def womOptionalTypeToOptionalTypeElement: CheckedAtoB[WomOptionalType, OptionalTypeElement] =
    CheckedAtoB.fromCheck { a: WomOptionalType =>
      womTypeToTypeElement(a.memberType) map { typeElement =>
        OptionalTypeElement(typeElement)
      }
    }

  def womPrimitiveTypeToPrimitiveTypeElement: CheckedAtoB[WomPrimitiveType, PrimitiveTypeElement] =
    CheckedAtoB.fromCheck { a: WomPrimitiveType =>
      PrimitiveTypeElement(a).validNelCheck
    }

  def expressionNodeToExpressionElement: CheckedAtoB[ExpressionNode, ExpressionElement] =
    CheckedAtoB.fromCheck {
      a: ExpressionNode => womExpressionToExpressionElement(a.womExpression)
    }

  def womExpressionToExpressionElement: CheckedAtoB[WomExpression, ExpressionElement] =
    CheckedAtoB.fromCheck {
      case a: WomExpression => ExpressionLiteralElement(a.sourceString).validNelCheck
      case a => invalidFromString(a.toString)
    }

  def inputDefinitionPointerToExpressionElement: CheckedAtoB[InputDefinitionPointer, Option[ExpressionElement]] =
    CheckedAtoB.fromCheck {
      // If the input definition is a node containing an expression, it's been declared explicitly
      case Inl(a: GraphNodeOutputPort) => a.graphNode match {
        case _: OptionalGraphInputNode =>
          None.validNelCheck
        case _: OptionalGraphInputNodeWithDefault =>
          None.validNelCheck
        case a: PlainAnonymousExpressionNode =>
          womExpressionToExpressionElement(a.womExpression) map { expressionElement =>
            Some(expressionElement)
          }
        case a: TaskCallInputExpressionNode =>
          womExpressionToExpressionElement(a.womExpression) map { expressionElement =>
            Some(expressionElement)
          }
        case _: RequiredGraphInputNode => None.validNelCheck
      }
      // Input definitions that directly contain expressions are the result of accepting a default input defined by the callable
      case Inr(Inl(_: WomExpression)) => None.validNelCheck
      case Inr(a) =>
        invalidFromString(a.toString)
      case a =>
        invalidFromString(a.toString)
    }

  def callNodeToCallElement: CheckedAtoB[CallNode, CallElement] =
    CheckedAtoB.fromCheck { call: CallNode =>
      def tupleToKvPair(tuple: (InputDefinition, InputDefinitionPointer)): Option[ExpressionElement.KvPair] = {
        inputDefinitionPointerToExpressionElement(tuple._2) match {
          case Right(Some(value)) => Some(ExpressionElement.KvPair(tuple._1.name, value))
          case _ => None
        }
      }

      val inputs = (call.inputDefinitionMappings flatMap tupleToKvPair).toVector

      val callableName = call.callable.name
      val callAlias = call.identifier.localName.value // If no alias, this is just the name; we evaluate for that below

      val maybeAlias = if (callableName != callAlias)
        Some(callAlias)
      else
        None

      val afters = call.nonInputBasedPrerequisites.map(_.localName).toVector

      CallElement(
        callableName,
        maybeAlias,
        afters,
        if (inputs.nonEmpty) Some(CallBodyElement(inputs)) else None,
        call.sourceLocation
      ).validNelCheck
    }
}


text:
@interface CashContext : CashSuper
@property (nonatomic, strong) CashSuper *casher;

- (CGFloat)getResult:(CGFloat)money;

@end