├── config ├── runtime.json ├── prod.js └── default.js ├── .gitignore ├── .npmignore ├── public ├── favicon.ico ├── img │ ├── 2w.jpg │ ├── 3w.jpg │ ├── 4w.jpg │ ├── man.png │ ├── beetle.png │ ├── blixw3.jpg │ ├── clock.png │ ├── croo5w.jpg │ ├── crown.png │ ├── horse.png │ ├── quote.gif │ ├── UfoDd4up.gif │ ├── UfoLa4up.gif │ ├── question.png │ ├── Oval3DLd0.gif │ ├── Wooshdown.gif │ ├── ajax-loader.gif │ ├── bu_rund2_8w.jpg │ ├── StarWarsLd3up.gif │ ├── SubmarineLf3.gif │ └── DataBrowserLj0up.gif ├── index.html ├── bbf.css ├── bbf.js └── monitor-min-all.min.js ├── History.md ├── README.md ├── package.json ├── LICENSE ├── app.js └── lib └── BBFProbe.js /config/runtime.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/favicon.ico -------------------------------------------------------------------------------- /public/img/2w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/2w.jpg -------------------------------------------------------------------------------- /public/img/3w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/3w.jpg -------------------------------------------------------------------------------- /public/img/4w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/4w.jpg -------------------------------------------------------------------------------- /public/img/man.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/man.png -------------------------------------------------------------------------------- /public/img/beetle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/beetle.png -------------------------------------------------------------------------------- /public/img/blixw3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/blixw3.jpg -------------------------------------------------------------------------------- /public/img/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/clock.png -------------------------------------------------------------------------------- /public/img/croo5w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/croo5w.jpg -------------------------------------------------------------------------------- /public/img/crown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/crown.png -------------------------------------------------------------------------------- /public/img/horse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/horse.png -------------------------------------------------------------------------------- /public/img/quote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/quote.gif -------------------------------------------------------------------------------- /public/img/UfoDd4up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/UfoDd4up.gif -------------------------------------------------------------------------------- /public/img/UfoLa4up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/UfoLa4up.gif -------------------------------------------------------------------------------- /public/img/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/question.png -------------------------------------------------------------------------------- /public/img/Oval3DLd0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/Oval3DLd0.gif -------------------------------------------------------------------------------- /public/img/Wooshdown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/Wooshdown.gif -------------------------------------------------------------------------------- /public/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/ajax-loader.gif -------------------------------------------------------------------------------- /public/img/bu_rund2_8w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/bu_rund2_8w.jpg -------------------------------------------------------------------------------- /config/prod.js: -------------------------------------------------------------------------------- 1 | // Production configurations 2 | module.exports = { 3 | BBF: { 4 | serverPort: 80 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/img/StarWarsLd3up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/StarWarsLd3up.gif -------------------------------------------------------------------------------- /public/img/SubmarineLf3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/SubmarineLf3.gif -------------------------------------------------------------------------------- /public/img/DataBrowserLj0up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenwest/big-button-factory/master/public/img/DataBrowserLj0up.gif -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 0.1.1 / 10-21-2012 2 | ================== 3 | 4 | * Separate basic view from view with sources 5 | * A better README.md 6 | 7 | 0.1.0 / 10-20-2012 8 | ================== 9 | 10 | * Initial checkin 11 | -------------------------------------------------------------------------------- /config/default.js: -------------------------------------------------------------------------------- 1 | // Default configurations. 2 | module.exports = { 3 | BBF: { 4 | serverPort: 3000 5 | }, 6 | MonitorMin: { 7 | appName: 'BigButtonFactory', 8 | allowExternalConnections: true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Big Button Factory 2 | ====================== 3 | 4 | Monitor-Min Demo App 5 | 6 | [![Build Status](https://secure.travis-ci.org/lorenwest/big-button-factory.png?branch=master)](https://travis-ci.org/lorenwest/big-button-factory) 7 | 8 | Introduction 9 | ------------ 10 | 11 | The Big Button Factory is a web app 12 | 13 | 14 | Quick Start 15 | ----------- 16 | 17 | **Install using npm** 18 | 19 | $ npm install big-button-factory 20 | 21 | **Run** 22 | 23 | $ node bbf 24 | 25 | 26 | We like big buttons ™ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "big-button-factory", 3 | "description": "Monitor-Min Demo App", 4 | "version": "0.1.1", 5 | "keywords": ["monitor", "node-monitor", "demo", "big-button-factory", "monitoring", "control"], 6 | "author": { 7 | "name": "Loren West", 8 | "email": "open_source@lorenwest.com", 9 | "url": "http://github.com/lorenwest" 10 | }, 11 | "homepage": "http://lorenwest.github.com/big-button-factory/", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/lorenwest/big-button-factory.git" 15 | }, 16 | "licenses": [ 17 | { 18 | "type": "MIT", 19 | "url": "https://github.com/lorenwest/big-button-factory/blob/master/LICENSE" 20 | } 21 | ], 22 | "dependencies": { 23 | "config": ">=0.4.17 <0.5.0", 24 | "express": ">=3.4.2 <3.5.0", 25 | "monitor-min": ">=0.5.11 <0.6.0", 26 | "config-monitor": ">=0.1.1 <0.2.0" 27 | }, 28 | "engines": { "node": ">= 0.4.0" }, 29 | "scripts": { 30 | "start": "node app.js" 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Loren West, and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var http = require('http'); 3 | var path = require('path'); 4 | var config = require('config'); 5 | var BBFProbe = require('./lib/BBFProbe'); 6 | var ConfigMonitor = require('config-monitor'); 7 | var Monitor = require('monitor-min').start(); 8 | var app = express(); 9 | 10 | 11 | app.use(express.static(path.join(__dirname, 'public'))); 12 | var appServer = http.createServer(app).listen(config.BBF.serverPort, function(){ 13 | 14 | // Splat 15 | console.log('' + 16 | ' ___ _ ___ __ __ ____ __ \n' + 17 | ' / _ )(_)__ _ / _ )__ __/ /_/ /____ ___ / __/__ _____/ /____ ______ __ \n' + 18 | ' / _ / / _ `/ / _ / // / __/ __/ _ \\/ _ \\ / _// _ `/ __/ __/ _ \\/ __/ // / \n' + 19 | '/____/_/\\_, / /____/\\_,_/\\__/\\__/\\___/_//_/ /_/ \\_,_/\\__/\\__/\\___/_/ \\_, / \n' + 20 | ' /___/ /___/'); 21 | console.log('Serving buttons on port ' + config.BBF.serverPort); 22 | 23 | // Add the monitor-min service to the existing app server 24 | var monitorService = new Monitor.Server({server:appServer}); 25 | monitorService.start(); 26 | }); 27 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Big Button Factory 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 35 |
36 |
37 |
38 |
39 |
40 |
41 |   42 |
43 | 47 |
48 | 49 | -------------------------------------------------------------------------------- /public/bbf.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | #header { 6 | background-color: rgb(21,130,171); 7 | padding: 4px 0 4px 0; 8 | } 9 | 10 | #header-inner { 11 | color: white; 12 | width: 960px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | #logo { 18 | float:left; 19 | width: 600px; 20 | font-size: 27px; 21 | text-shadow: #FFF 1px 1px 5px; 22 | } 23 | 24 | #auth { 25 | float:left; 26 | width: 360px; 27 | margin-top: 10px; 28 | font-size: 14px; 29 | } 30 | 31 | #entry { 32 | float: right; 33 | margin-top: -8px; 34 | } 35 | 36 | #name { 37 | width: 170px; 38 | margin-top: 4px; 39 | } 40 | 41 | #begin { 42 | margin-top: 4px; 43 | } 44 | 45 | #photo { 46 | height: 34px; 47 | width: 34px; 48 | border: 1px solid #aaa; 49 | border-radius: 4px; 50 | margin-top: -3px; 51 | cursor: pointer; 52 | } 53 | 54 | #center { 55 | width: 960px; 56 | padding-top: 20px; 57 | margin-left: auto; 58 | margin-right: auto; 59 | } 60 | 61 | #left { 62 | float: left; 63 | width: 600px; 64 | } 65 | 66 | #right { 67 | float: left; 68 | width: 360px; 69 | } 70 | 71 | .tray { 72 | margin-bottom: 10px; 73 | width: 576px; 74 | background-color: #EEE; 75 | border: 1px solid #CCC; 76 | border-radius: 4px; 77 | padding: 4px 0 4px 10px; 78 | } 79 | 80 | .tray-name { 81 | font-size: 20px; 82 | text-shadow: #888 1px 0px 1px; 83 | } 84 | 85 | .bbf-button { 86 | cursor: pointer; 87 | float:left; 88 | border-radius: 4px; 89 | margin: 4px 8px 4px 0px; 90 | height: 48px; width: 48px; 91 | background-size:48px 48px; 92 | background-repeat:no-repeat; 93 | } 94 | 95 | .bbf-button:hover { 96 | opacity: 1; 97 | } 98 | 99 | .bbf-button.pushed { 100 | margin: 11px 19px 9px 1px; 101 | } 102 | 103 | .B1 { 104 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/StarWarsLd3up.gif); 105 | } 106 | .B2 { 107 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/DataBrowserLj0up.gif); 108 | } 109 | .B3 { 110 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/Oval3DLd0.gif); 111 | } 112 | .B4 { 113 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/SubmarineLf3.gif); 114 | } 115 | .B5 { 116 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/UfoDd4up.gif); 117 | } 118 | .B6 { 119 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/UfoLa4up.gif); 120 | } 121 | .B7 { 122 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/Wooshdown.gif); 123 | } 124 | .B8 { 125 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/quote.gif); 126 | } 127 | .B9 { 128 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/2w.jpg); 129 | } 130 | .B10 { 131 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/3w.jpg); 132 | } 133 | .B11 { 134 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/4w.jpg); 135 | } 136 | .B12 { 137 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/clock.png); 138 | } 139 | .B13 { 140 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/beetle.png); 141 | } 142 | .B14 { 143 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/blixw3.jpg); 144 | } 145 | .B15 { 146 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/bu_rund2_8w.jpg); 147 | } 148 | .B16 { 149 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/croo5w.jpg); 150 | } 151 | .B17 { 152 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/crown.png); 153 | } 154 | .B18 { 155 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/horse.png); 156 | } 157 | .B19 { 158 | background-image: url(http://rawgithub.com/lorenwest/big-button-factory/master/public/img/man.png); 159 | } 160 | 161 | /* mobile */ 162 | @media (max-width: 960px){ 163 | #header-inner { 164 | width: 640px; 165 | } 166 | #center { 167 | width: 640px; 168 | } 169 | } -------------------------------------------------------------------------------- /public/bbf.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | // Constants 4 | var BUTTONS_PER_TRAY = 20, 5 | NUM_TRAYS = 4, 6 | AJAX_LOADER = '/img/ajax-loader.gif', 7 | HELP_IMG = '/img/question.png'; 8 | 9 | // Preload images 10 | $('')[0].src = AJAX_LOADER; 11 | 12 | // Create the BBF namespace 13 | var BBF = {trays:{}}; 14 | 15 | // Button Tray Class 16 | BBF.Tray = function(options) { 17 | var t = this; 18 | t.num = options.num; 19 | t.name = options.name; 20 | t.buttonClass = options.buttonClass; 21 | 22 | // Build the HTML 23 | var html = '
'; 24 | html += '
' + t.name + '
'; 25 | for (var i = 0; i < BUTTONS_PER_TRAY; i++) { 26 | html += '
 
'; 27 | } 28 | html += '
'; 29 | $(html).appendTo('#Tray' + t.num + 'Container'); 30 | }; 31 | 32 | // Connect to the backend monitor that represents the current button trays 33 | window.BBFMonitor = new Monitor({probeClass: 'BBFProbe'}); 34 | BBFMonitor.connect(function(error){ 35 | 36 | // Add the trays 37 | for (var trayNum = 0; trayNum < NUM_TRAYS; trayNum++) { 38 | 39 | // Visualize the tray 40 | var trayId = 'Tray' + trayNum; 41 | var monitorTray = BBFMonitor.get(trayId); 42 | BBF.trays[trayId] = new BBF.Tray({ 43 | num: trayNum, 44 | name: monitorTray.name, 45 | buttonClass: monitorTray.buttonClass 46 | }); 47 | } 48 | 49 | }); 50 | 51 | // Handle a button click 52 | $('#left').delegate('.bbf-button', 'click', function(event) { 53 | var buttonId = event.currentTarget.id, 54 | photoUrl = window.photoUrl, 55 | currentUrl = $('#' + buttonId).attr('src'); 56 | 57 | if (!photoUrl) { 58 | alert('Enter your Profile Photo to push buttons') 59 | return; 60 | } 61 | 62 | // Don't allow a click if it's already set 63 | if ($('#' + buttonId).attr('src').length > 0) { 64 | return; 65 | } 66 | 67 | // Tell the backend the button was pushed 68 | BBFMonitor.control('buttonPushed', {buttonId: buttonId, photoUrl: photoUrl}); 69 | }); 70 | 71 | // Notify me when the BBFMonitor changes 72 | BBFMonitor.on('change', function(){ 73 | 74 | // Process all changed attributes 75 | var changedAttrs = BBFMonitor.changedAttributes(); 76 | for (attrName in changedAttrs) { 77 | 78 | // Has a tray changed? 79 | if (attrName.indexOf('Tray') === 0 && changedAttrs[attrName].name != BBF.trays[attrName].name) { 80 | var trayId = attrName, 81 | trayNum = trayId.substr(4,1), 82 | selector = '#' + trayId, 83 | oldTray = $(selector); 84 | 85 | // Animate the old tray out 86 | oldTray.animate({marginLeft: -2000}, 360, function() { 87 | oldTray.remove(); 88 | var monitorTray = BBFMonitor.get(trayId); 89 | BBF.trays[trayId] = new BBF.Tray({ 90 | num: trayNum, 91 | name: monitorTray.name, 92 | buttonClass: monitorTray.buttonClass 93 | }); 94 | 95 | // Animate the new tray in 96 | $(selector).css({marginLeft:-2000}).animate({marginLeft: 0}, 360); 97 | }); 98 | } 99 | 100 | // Has a button changed? 101 | if (attrName.indexOf('B') === 0) { 102 | var buttonId = attrName; 103 | var pictureUrl = changedAttrs[attrName]; 104 | if (pictureUrl.length > 0) { 105 | $('#' + buttonId).attr('src', pictureUrl).css('backgroundImage', 'url(' + pictureUrl + ')'); 106 | } 107 | } 108 | } 109 | }); 110 | 111 | // Show the help screen 112 | $('#photo').on('click', function() { 113 | alert('Add your profile photo to start.\n\nThis can be your github name,\nyour @twitterName,\nyour gravatar email address,\nor any photo URL starting with http://'); 114 | }); 115 | 116 | // Show the users photo, save to localStorage, and make global 117 | var showPhoto = function(userName, photoUrl) { 118 | $('#name').val(userName); 119 | $('#photo').attr('src', photoUrl); 120 | localStorage.photoUrl = photoUrl; 121 | localStorage.userName = userName; 122 | window.photoUrl = photoUrl; 123 | } 124 | 125 | 126 | // Process the user name into their photo URL 127 | $('#begin').click(function(){ 128 | var userName = $('#name').val(), 129 | trimmedName = userName.trim(), 130 | nameType = null; 131 | 132 | // Get the user name and 133 | if (!userName) { 134 | showPhoto('',HELP_IMG); 135 | window.photoUrl = null; 136 | return; 137 | } 138 | 139 | // Is it an url to a photo? 140 | if (trimmedName.match(/^http[s]?:\/\//i)) { 141 | return showPhoto(trimmedName, trimmedName); 142 | } 143 | 144 | // It's a twitter name if it starts with a @ 145 | if (trimmedName.match(/^@/)) { 146 | trimmedName = trimmedName.substr(1); 147 | nameType = 'twitter'; 148 | } 149 | 150 | // It's a gravatar name if it contains an @ 151 | else if (userName.match(/@/)) { 152 | nameType = 'gravatar'; 153 | } 154 | 155 | // Otherwise it's a github name 156 | else { 157 | nameType = 'github'; 158 | } 159 | 160 | // Load the ajax spinner 161 | $('#photo').attr('src', AJAX_LOADER); 162 | 163 | // Tell the backend the button was pushed 164 | BBFMonitor.control('findPhotoUrl', {type: nameType, name: trimmedName}, function(err, photoUrl){ 165 | if (err) { 166 | showPhoto('',HELP_IMG); 167 | alert('Oops - we couldn\'t find that account.'); 168 | return; 169 | } 170 | showPhoto(userName, photoUrl); 171 | }); 172 | 173 | }); 174 | $('#name').focus(); 175 | 176 | // Set the photo url if it's found in local storage 177 | if (localStorage.photoUrl) { 178 | showPhoto(localStorage.userName, localStorage.photoUrl); 179 | } 180 | 181 | }); 182 | -------------------------------------------------------------------------------- /lib/BBFProbe.js: -------------------------------------------------------------------------------- 1 | // BBFProbe.js 2 | (function(root) { 3 | 4 | // Module loading - this runs server-side only 5 | var Monitor = require('monitor-min'), 6 | Backbone = Monitor.Backbone, 7 | Probe = Monitor.Probe, 8 | _ = Monitor._, 9 | logger = Monitor.getLogger('BBFProbe'), 10 | crypto = require('crypto'), 11 | exec = require('child_process').exec, 12 | CONFIG = require('config'); 13 | 14 | // Constants 15 | var NUM_TRAYS = 4; 16 | var BUTTONS_PER_TRAY = 20; 17 | var TRAY_TYPES = [ 18 | 19 | {name: 'Crown Royal', buttonClass: 'B17'}, 20 | {name: 'Force Field', buttonClass: 'B16'}, 21 | {name: 'Line Dancer', buttonClass: 'B19'}, 22 | {name: 'Grandma\'s Purple', buttonClass: 'B4'}, 23 | 24 | {name: 'Dancing Ladies', buttonClass: 'B13'}, 25 | {name: '4-Star Spectacular', buttonClass: 'B14'}, 26 | {name: 'Brushed Aluminum', buttonClass: 'B15'}, 27 | 28 | {name: 'Into the Knight', buttonClass: 'B18'}, 29 | {name: 'Gold Digger', buttonClass: 'B9'}, 30 | {name: 'Frog\'s Eye', buttonClass: 'B10'}, 31 | {name: 'Sore Throat', buttonClass: 'B11'}, 32 | {name: 'Time\'s Up', buttonClass: 'B12'}, 33 | 34 | 35 | {name: 'Depressed & Blue', buttonClass: 'B7'}, 36 | {name: 'Bubblegum on your Shoe', buttonClass: 'B8'}, 37 | {name: 'Stargate', buttonClass: 'B1'}, 38 | {name: 'Spaceship Portal', buttonClass: 'B2'}, 39 | {name: 'Radio Head', buttonClass: 'B6'}, 40 | {name: 'Mech Warrior', buttonClass: 'B5'}, 41 | {name: 'Vampire Bite', buttonClass: 'B3'} 42 | ]; 43 | 44 | /** 45 | * The BBF probe represents the current state of the button trays 46 | * and all buttons in them. 47 | * 48 | * @class BBFProbe 49 | * @extends Probe 50 | * @constructor 51 | * @param model - Monitor data model elements 52 | * @param model.Tray[0-n] {Object} Objects representing each tray 53 | * @param model.B[trayNum]_[0-n] {String} Blank if button hasnt been pushed, 54 | * or the md5 hash of the gravatar email of the button pusher. 55 | */ 56 | var BBFProbe = Probe.extend({ 57 | 58 | // Exposed probeClass name for monitors 59 | probeClass: 'BBFProbe', 60 | 61 | initialize: function(){ 62 | var t = this; 63 | Probe.prototype.initialize.apply(t, arguments); 64 | 65 | // Set the initial button type to use 66 | t.nextTrayTypeNum = Math.floor(Math.random() * TRAY_TYPES.length); 67 | 68 | // Create the initial trays 69 | for (var trayNum = 0; trayNum < NUM_TRAYS; trayNum++) { 70 | t.newTray(trayNum); 71 | } 72 | 73 | // Expose this probe to global 74 | global.BBFProbe = t; 75 | }, 76 | 77 | /** 78 | * Build a new tray 79 | * 80 | * This method removes any prior tray data, and populates a new tray with 81 | * either the next type of tray, or the specified tray type 82 | */ 83 | newTray: function(trayNum, trayTypeNum) { 84 | var t = this; 85 | 86 | // Get the tray type 87 | if (_.isUndefined(trayTypeNum)) { 88 | trayTypeNum = t.nextTrayTypeNum++; 89 | if (t.nextTrayTypeNum >= TRAY_TYPES.length) { 90 | t.nextTrayTypeNum = 0; 91 | } 92 | } 93 | var trayType = TRAY_TYPES[trayTypeNum]; 94 | 95 | // Assure a new tray type 96 | if (t.get('Tray' + trayNum) && t.get('Tray' + trayNum).name == trayType.name) { 97 | t.newTray(trayNum); 98 | return; 99 | } 100 | 101 | // Populate the tray 102 | t.set('Tray' + trayNum, trayType); 103 | 104 | // Populate the buttons 105 | for (var i = 0; i < BUTTONS_PER_TRAY; i ++) { 106 | t.set('B' + trayNum + '_' + i, ''); 107 | } 108 | 109 | // Log 110 | logger.info('newTray', {trayNum:trayNum, trayTypeNum:trayTypeNum}); 111 | }, 112 | 113 | /** 114 | * Replace a button tray (probe control) 115 | * 116 | * @param options {object} Options 117 | * @param options.trayNum {Number} The tray number to replace 118 | */ 119 | newTray_control: function(trayNum, callback) { 120 | var t = this; 121 | trayNum = trayNum ? trayNum : 0; 122 | t.newTray(trayNum); 123 | callback(null); 124 | }, 125 | 126 | /** 127 | * This control method is called from the browser when a big button is pushed 128 | * 129 | * @param options {object} Options 130 | * @param options.buttonId {String} ID of the button pushed 131 | * @param options.photoUrl {String} The URL of the person that pushed the button 132 | * @param [callback] {function(err)} Callback 133 | */ 134 | buttonPushed_control: function(options, callback) { 135 | var t = this, 136 | trayId = options.buttonId.substr(1,1); 137 | 138 | // Set the photo into the button 139 | t.set(options.buttonId, options.photoUrl); 140 | 141 | // Let the picture get updated, then check if 142 | // this is the last button pushed in the tray 143 | setTimeout(function(){ 144 | 145 | // Get a new tray if all buttons are pushed 146 | var numPushed = 0; 147 | for (var i = 0; i < BUTTONS_PER_TRAY; i++) { 148 | if (t.get('B' + trayId + '_' + i).length > 0) { 149 | numPushed++; 150 | } 151 | } 152 | if (numPushed == BUTTONS_PER_TRAY) { 153 | t.newTray(trayId); 154 | } 155 | 156 | callback(); 157 | }, 100); 158 | }, 159 | 160 | /** 161 | * This attempts to convert a name into a photo URL for that person 162 | * 163 | * The 'github' type is the github name. 'gravatar' type is the gravatar 164 | * email address, and 'twitter' is their twitter name without the '@'. 165 | * 166 | * @param options {object} Options 167 | * @param options.type {String} One of "github", "gravatar", or "twitter" 168 | * @param options.name {String} The URL of the person that pushed the button 169 | * @param callback {Function(err, url)} The callback function 170 | * @param callback.err {Object} The error object if an error occurred 171 | * @param callback.url {String} The photo URL for that person 172 | */ 173 | findPhotoUrl_control: function(options, callback) { 174 | var t = this, 175 | type = options.type, 176 | name = options.name, 177 | url = null; 178 | 179 | // Gravatar: Return the MD5 hash of their lowercased, trimmed email 180 | if (type === 'gravatar') { 181 | var md5 = crypto.createHash('md5'); 182 | md5.update(name.toLowerCase().trim()); 183 | url = 'http://www.gravatar.com/avatar/' + md5.digest('hex') + '.jpg'; 184 | return callback(null, url); 185 | } 186 | 187 | // Twitter name 188 | else if (type === 'twitter') { 189 | url = 'https://twitter.com/' + name; 190 | t.getAvatarImgLine(url, 'avatar size73', function(err, avatarUrl) { 191 | if (err) { 192 | return callback(err); 193 | } 194 | 195 | // Parse the http portion 196 | var part = avatarUrl.substr(avatarUrl.indexOf('http')); 197 | part = part.substr(0, part.indexOf('"')); 198 | return callback(err, part); 199 | }); 200 | } 201 | 202 | // Github name 203 | else if (type === 'github') { 204 | url = 'https://github.com/' + name; 205 | t.getAvatarImgLine(url, '2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?null:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or||n===void 0)return 1;if(n>>1;n.call(r,e[u])=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&(t[t.length]=n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]==null&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var O=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=O(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&O(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return O(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i":">",'"':""","'":"'","/":"/"}};M.unescape=x.invert(M.escape);var _={escape:new RegExp("["+x.keys(M.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(M.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(_[e],function(t){return M[e][t]})}}),x.result=function(e,t){if(e==null)return null;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),j.call(this,n.apply(x,e))}})};var D=0;x.uniqueId=function(e){var t=++D+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var P=/(.)^/,H={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||P).source,(n.interpolate||P).source,(n.evaluate||P).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(B,function(e){return"\\"+H[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var j=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],j.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return j.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),function(){var e=this,t=e.Backbone,n=[],r=n.push,i=n.slice,s=n.splice,o;typeof exports!="undefined"?o=exports:o=e.Backbone={},o.VERSION="0.9.9";var u=e._;!u&&typeof require!="undefined"&&(u=require("underscore")),o.$=e.jQuery||e.Zepto||e.ender,o.noConflict=function(){return e.Backbone=t,this},o.emulateHTTP=!1,o.emulateJSON=!1;var a=/\s+/,f=function(e,t,n,r){if(!n)return!0;if(typeof n=="object")for(var i in n)e[t].apply(e,[i,n[i]].concat(r));else{if(!a.test(n))return!0;var s=n.split(a);for(var o=0,u=s.length;o=0;r-=2)this.trigger("change:"+n[r],this,n[r+1],e);if(t)return this;while(this._pending)this._pending=!1,this.trigger("change",this,e),this._previousAttributes=u.clone(this.attributes);return this._changing=!1,this},hasChanged:function(e){return this._hasComputed||this._computeChanges(),e==null?!u.isEmpty(this.changed):u.has(this.changed,e)},changedAttributes:function(e){if(!e)return this.hasChanged()?u.clone(this.changed):!1;var t,n=!1,r=this._previousAttributes;for(var i in e){if(u.isEqual(r[i],t=e[i]))continue;(n||(n={}))[i]=t}return n},_computeChanges:function(e){this.changed={};var t={},n=[],r=this._currentAttributes,i=this._changes;for(var s=i.length-2;s>=0;s-=2){var o=i[s],u=i[s+1];if(t[o])continue;t[o]=!0;if(r[o]!==u){this.changed[o]=u;if(!e)continue;n.push(o,u),r[o]=u}}return e&&(this._changes=[]),this._hasComputed=!0,n},previous:function(e){return e==null||!this._previousAttributes?null:this._previousAttributes[e]},previousAttributes:function(){return u.clone(this._previousAttributes)},_validate:function(e,t){if(!this.validate)return!0;e=u.extend({},this.attributes,e);var n=this.validate(e,t);return n?(t&&t.error&&t.error(this,n,t),this.trigger("error",this,n,t),!1):!0}});var p=o.Collection=function(e,t){t||(t={}),t.model&&(this.model=t.model),t.comparator!==void 0&&(this.comparator=t.comparator),this._reset(),this.initialize.apply(this,arguments),e&&this.reset(e,u.extend({silent:!0},t))};u.extend(p.prototype,c,{model:h,initialize:function(){},toJSON:function(e){return this.map(function(t){return t.toJSON(e)})},sync:function(){return o.sync.apply(this,arguments)},add:function(e,t){var n,i,o,a,f,l,c=t&&t.at,h=(t&&t.sort)==null?!0:t.sort;e=u.isArray(e)?e.slice():[e];for(n=e.length-1;n>=0;n--){if(!(a=this._prepareModel(e[n],t))){this.trigger("error",this,e[n],t),e.splice(n,1);continue}e[n]=a,f=a.id!=null&&this._byId[a.id];if(f||this._byCid[a.cid]){t&&t.merge&&f&&(f.set(a.attributes,t),l=h),e.splice(n,1);continue}a.on("all",this._onModelEvent,this),this._byCid[a.cid]=a,a.id!=null&&(this._byId[a.id]=a)}e.length&&(l=h),this.length+=e.length,i=[c!=null?c:this.models.length,0],r.apply(i,e),s.apply(this.models,i),l&&this.comparator&&c==null&&this.sort({silent:!0});if(t&&t.silent)return this;while(a=e.shift())a.trigger("add",a,this,t);return this},remove:function(e,t){var n,r,i,s;t||(t={}),e=u.isArray(e)?e.slice():[e];for(n=0,r=e.length;n').hide().appendTo("body")[0].contentWindow,this.navigate(t)),this._hasPushState?o.$(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!r?o.$(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval)),this.fragment=t;var i=this.location,s=i.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!s)return this.fragment=this.getFragment(null,!0),this.location.replace(this.root+this.location.search+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&s&&i.hash&&(this.fragment=this.getHash().replace(S,""),this.history.replaceState({},document.title,this.root+this.fragment+i.search));if(!this.options.silent)return this.loadUrl()},stop:function(){o.$(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl),clearInterval(this._checkUrlInterval),E.started=!1},route:function(e,t){this.handlers.unshift({route:e,callback:t})},checkUrl:function(e){var t=this.getFragment();t===this.fragment&&this.iframe&&(t=this.getFragment(this.getHash(this.iframe)));if(t===this.fragment)return!1;this.iframe&&this.navigate(t),this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(e){var t=this.fragment=this.getFragment(e),n=u.any(this.handlers,function(e){if(e.route.test(t))return e.callback(t),!0});return n},navigate:function(e,t){if(!E.started)return!1;if(!t||t===!0)t={trigger:t};e=this.getFragment(e||"");if(this.fragment===e)return;this.fragment=e;var n=this.root+e;if(this._hasPushState)this.history[t.replace?"replaceState":"pushState"]({},document.title,n);else{if(!this._wantsHashChange)return this.location.assign(n);this._updateHash(this.location,e,t.replace),this.iframe&&e!==this.getFragment(this.getHash(this.iframe))&&(t.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,e,t.replace))}t.trigger&&this.loadUrl(e)},_updateHash:function(e,t,n){if(n){var r=e.href.replace(/(javascript:|#).*$/,"");e.replace(r+"#"+t)}else e.hash="#"+t}}),o.history=new E;var C=o.View=function(e){this.cid=u.uniqueId("view"),this._configure(e||{}),this._ensureElement(),this.initialize.apply(this,arguments),this.delegateEvents()},k=/^(\S+)\s*(.*)$/,L=["model","collection","el","id","attributes","className","tagName","events"];u.extend(C.prototype,c,{tagName:"div",$:function(e){return this.$el.find(e)},initialize:function(){},render:function(){return this},remove:function(){return this.$el.remove(),this.stopListening(),this},make:function(e,t,n){var r=document.createElement(e);return t&&o.$(r).attr(t),n!=null&&o.$(r).html(n),r},setElement:function(e,t){return this.$el&&this.undelegateEvents(),this.$el=e instanceof o.$?e:o.$(e),this.el=this.$el[0],t!==!1&&this.delegateEvents(),this},delegateEvents:function(e){if(!e&&!(e=u.result(this,"events")))return;this.undelegateEvents();for(var t in e){var n=e[t];u.isFunction(n)||(n=this[e[t]]);if(!n)throw new Error('Method "'+e[t]+'" does not exist');var r=t.match(k),i=r[1],s=r[2];n=u.bind(n,this),i+=".delegateEvents"+this.cid,s===""?this.$el.bind(i,n):this.$el.delegate(s,i,n)}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(e){this.options&&(e=u.extend({},u.result(this,"options"),e)),u.extend(this,u.pick(e,L)),this.options=e},_ensureElement:function(){if(!this.el){var e=u.extend({},u.result(this,"attributes"));this.id&&(e.id=u.result(this,"id")),this.className&&(e["class"]=u.result(this,"className")),this.setElement(this.make(u.result(this,"tagName"),e),!1)}else this.setElement(u.result(this,"el"),!1)}});var A={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};o.sync=function(e,t,n){var r=A[e];u.defaults(n||(n={}),{emulateHTTP:o.emulateHTTP,emulateJSON:o.emulateJSON});var i={type:r,dataType:"json"};n.url||(i.url=u.result(t,"url")||M()),n.data==null&&t&&(e==="create"||e==="update"||e==="patch")&&(i.contentType="application/json",i.data=JSON.stringify(n.attrs||t.toJSON(n))),n.emulateJSON&&(i.contentType="application/x-www-form-urlencoded",i.data=i.data?{model:i.data}:{});if(n.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){i.type="POST",n.emulateJSON&&(i.data._method=r);var s=n.beforeSend;n.beforeSend=function(e){e.setRequestHeader("X-HTTP-Method-Override",r);if(s)return s.apply(this,arguments)}}i.type!=="GET"&&!n.emulateJSON&&(i.processData=!1);var a=n.success;n.success=function(e,r,i){a&&a(e,r,i),t.trigger("sync",t,e,n)};var f=n.error;n.error=function(e,r,i){f&&f(t,e,n),t.trigger("error",t,e,n)};var l=o.ajax(u.extend(i,n));return t.trigger("request",t,l,n),l},o.ajax=function(){return o.$.ajax.apply(o.$,arguments)};var O=function(e,t){var n=this,r;e&&u.has(e,"constructor")?r=e.constructor:r=function(){n.apply(this,arguments)},u.extend(r,n,t);var i=function(){this.constructor=r};return i.prototype=n.prototype,r.prototype=new i,e&&u.extend(r.prototype,e),r.__super__=n.prototype,r};h.extend=p.extend=m.extend=C.extend=E.extend=O;var M=function(){throw new Error('A "url" property or function must be specified')}}.call(this),function(e){var t=e._||require("underscore")._,n=e.Backbone||require("backbone"),r=function(e,n){return function(){var r=t.toArray(arguments),i=r[r.length-1];if(typeof i=="function"){r.splice(-1,1),r.length===0&&r.push({}),r.length===1&&e==="save"&&r.push({});var s=r[r.length-1];s.success=function(e,t){i(null,t)},s.error=function(e,t){i(t,null)}}return n.apply(this,r)}};typeof module!="undefined"&&typeof module.exports!="undefined"?module.exports=r:e.BackboneCallbacks=r,r.attach=function(e){t.each(["save","destroy","fetch"],function(t){e.Model.prototype[t]=new r(t,e.Model.prototype[t])}),t.each(["fetch"],function(t){e.Collection.prototype[t]=new r(t,e.Collection.prototype[t])})},r.attach(n)}(this);var io="undefined"==typeof module?{}:module.exports;(function(){(function(e,t){var n=e;n.version="0.9.11",n.protocol=1,n.transports=[],n.j=[],n.sockets={},n.connect=function(e,r){var i=n.util.parseUri(e),s,o;t&&t.location&&(i.protocol=i.protocol||t.location.protocol.slice(0,-1),i.host=i.host||(t.document?t.document.domain:t.location.hostname),i.port=i.port||t.location.port),s=n.util.uniqueUri(i);var u={host:i.host,secure:"https"==i.protocol,port:i.port||("https"==i.protocol?443:80),query:i.query||""};n.util.merge(u,r);if(u["force new connection"]||!n.sockets[s])o=new n.Socket(u);return!u["force new connection"]&&o&&(n.sockets[s]=o),o=o||n.sockets[s],o.of(i.path.length>1?i.path:"")}})("object"==typeof module?module.exports:this.io={},this),function(e,t){var n=e.util={},r=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,i=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];n.parseUri=function(e){var t=r.exec(e||""),n={},s=14;while(s--)n[i[s]]=t[s]||"";return n},n.uniqueUri=function(e){var n=e.protocol,r=e.host,i=e.port;return"document"in t?(r=r||document.domain,i=i||(n=="https"&&document.location.protocol!=="https:"?443:document.location.port)):(r=r||"localhost",!i&&n=="https"&&(i=443)),(n||"http")+"://"+r+":"+(i||80)},n.query=function(e,t){var r=n.chunkQuery(e||""),i=[];n.merge(r,n.chunkQuery(t||""));for(var s in r)r.hasOwnProperty(s)&&i.push(s+"="+r[s]);return i.length?"?"+i.join("&"):""},n.chunkQuery=function(e){var t={},n=e.split("&"),r=0,i=n.length,s;for(;rt.length?e:t,s=e.length>t.length?t:e;for(var o=0,u=s.length;o0&&e.splice(0,1)[0]!=n.transport.name);e.length?u(e):n.publish("connect_failed")}}},n.options["connect timeout"]))})}n.sessionid=r,n.closeTimeout=s*1e3,n.heartbeatTimeout=i*1e3,n.transports||(n.transports=n.origTransports=o?t.util.intersect(o.split(","),n.options.transports):n.options.transports),n.setHeartbeatTimeout(),u(n.transports),n.once("connect",function(){clearTimeout(n.connectTimeoutTimer),e&&typeof e=="function"&&e()})}),this},r.prototype.setHeartbeatTimeout=function(){clearTimeout(this.heartbeatTimeoutTimer);if(this.transport&&!this.transport.heartbeats())return;var e=this;this.heartbeatTimeoutTimer=setTimeout(function(){e.transport.onClose()},this.heartbeatTimeout)},r.prototype.packet=function(e){return this.connected&&!this.doBuffer?this.transport.packet(e):this.buffer.push(e),this},r.prototype.setBuffer=function(e){this.doBuffer=e,!e&&this.connected&&this.buffer.length&&(this.options.manualFlush||this.flushBuffer())},r.prototype.flushBuffer=function(){this.transport.payload(this.buffer),this.buffer=[]},r.prototype.disconnect=function(){if(this.connected||this.connecting)this.open&&this.of("").packet({type:"disconnect"}),this.onDisconnect("booted");return this},r.prototype.disconnectSync=function(){var e=t.util.request(),n=["http"+(this.options.secure?"s":"")+":/",this.options.host+":"+this.options.port,this.options.resource,t.protocol,"",this.sessionid].join("/")+"/?disconnect=1";e.open("GET",n,!1),e.send(null),this.onDisconnect("booted")},r.prototype.isXDomain=function(){var e=n.location.port||("https:"==n.location.protocol?443:80);return this.options.host!==n.location.hostname||this.options.port!=e},r.prototype.onConnect=function(){this.connected||(this.connected=!0,this.connecting=!1,this.doBuffer||this.setBuffer(!1),this.emit("connect"))},r.prototype.onOpen=function(){this.open=!0},r.prototype.onClose=function(){this.open=!1,clearTimeout(this.heartbeatTimeoutTimer)},r.prototype.onPacket=function(e){this.of(e.endpoint).onPacket(e)},r.prototype.onError=function(e){e&&e.advice&&e.advice==="reconnect"&&(this.connected||this.connecting)&&(this.disconnect(),this.options.reconnect&&this.reconnect()),this.publish("error",e&&e.reason?e.reason:e)},r.prototype.onDisconnect=function(e){var t=this.connected,n=this.connecting;this.connected=!1,this.connecting=!1,this.open=!1;if(t||n)this.transport.close(),this.transport.clearTimeouts(),t&&(this.publish("disconnect",e),"booted"!=e&&this.options.reconnect&&!this.reconnecting&&this.reconnect())},r.prototype.reconnect=function(){function i(){if(e.connected){for(var t in e.namespaces)e.namespaces.hasOwnProperty(t)&&""!==t&&e.namespaces[t].packet({type:"connect"});e.publish("reconnect",e.transport.name,e.reconnectionAttempts)}clearTimeout(e.reconnectionTimer),e.removeListener("connect_failed",s),e.removeListener("connect",s),e.reconnecting=!1,delete e.reconnectionAttempts,delete e.reconnectionDelay,delete e.reconnectionTimer,delete e.redoTransports,e.options["try multiple transports"]=n}function s(){if(!e.reconnecting)return;if(e.connected)return i();if(e.connecting&&e.reconnecting)return e.reconnectionTimer=setTimeout(s,1e3);e.reconnectionAttempts++>=t?e.redoTransports?(e.publish("reconnect_failed"),i()):(e.on("connect_failed",s),e.options["try multiple transports"]=!0,e.transports=e.origTransports,e.transport=e.getTransport(),e.redoTransports=!0,e.connect()):(e.reconnectionDelay=10:!1},n.xdomainCheck=function(){return!0},typeof window!="undefined"&&(WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0),t.transports.push("flashsocket")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports);if("undefined"!=typeof window)var swfobject=function(){function C(){if(b)return;try{var e=a.getElementsByTagName("body")[0].appendChild(U("span"));e.parentNode.removeChild(e)}catch(t){return}b=!0;var n=c.length;for(var r=0;r0)for(var n=0;n0){var o=R(r);if(o)if(W(h[n].swfVersion)&&!(T.wk&&T.wk<312))V(r,!0),i&&(s.success=!0,s.ref=_(r),i(s));else if(h[n].expressInstall&&D()){var u={};u.data=h[n].expressInstall,u.width=o.getAttribute("width")||"0",u.height=o.getAttribute("height")||"0",o.getAttribute("class")&&(u.styleclass=o.getAttribute("class")),o.getAttribute("align")&&(u.align=o.getAttribute("align"));var a={},f=o.getElementsByTagName("param"),l=f.length;for(var c=0;c');u.outerHTML='"+l+"",p[p.length]=n.id,o=R(n.id)}else{var h=U(t);h.setAttribute("type",i);for(var d in n)n[d]!=Object.prototype[d]&&(d.toLowerCase()=="styleclass"?h.setAttribute("class",n[d]):d.toLowerCase()!="classid"&&h.setAttribute(d,n[d]));for(var v in r)r[v]!=Object.prototype[v]&&v.toLowerCase()!="movie"&&F(h,v,r[v]);u.parentNode.replaceChild(h,u),o=h}}return o}function F(e,t,n){var r=U("param");r.setAttribute("name",t),r.setAttribute("value",n),e.appendChild(r)}function I(e){var t=R(e);t&&t.nodeName=="OBJECT"&&(T.ie&&T.win?(t.style.display="none",function(){t.readyState==4?q(e):setTimeout(arguments.callee,10)}()):t.parentNode.removeChild(t))}function q(e){var t=R(e);if(t){for(var n in t)typeof t[n]=="function"&&(t[n]=null);t.parentNode.removeChild(t)}}function R(e){var t=null;try{t=a.getElementById(e)}catch(n){}return t}function U(e){return a.createElement(e)}function z(e,t,n){e.attachEvent(t,n),d[d.length]=[e,t,n]}function W(e){var t=T.pv,n=e.split(".");return n[0]=parseInt(n[0],10),n[1]=parseInt(n[1],10)||0,n[2]=parseInt(n[2],10)||0,t[0]>n[0]||t[0]==n[0]&&t[1]>n[1]||t[0]==n[0]&&t[1]==n[1]&&t[2]>=n[2]?!0:!1}function X(n,r,i,s){if(T.ie&&T.mac)return;var o=a.getElementsByTagName("head")[0];if(!o)return;var u=i&&typeof i=="string"?i:"screen";s&&(E=null,S=null);if(!E||S!=u){var f=U("style");f.setAttribute("type","text/css"),f.setAttribute("media",u),E=o.appendChild(f),T.ie&&T.win&&typeof a.styleSheets!=e&&a.styleSheets.length>0&&(E=a.styleSheets[a.styleSheets.length-1]),S=u}T.ie&&T.win?E&&typeof E.addRule==t&&E.addRule(n,r):E&&typeof a.createTextNode!=e&&E.appendChild(a.createTextNode(n+" {"+r+"}"))}function V(e,t){if(!x)return;var n=t?"visible":"hidden";b&&R(e)?R(e).style.visibility=n:X("#"+e,"visibility:"+n)}function $(t){var n=/[\\\"<>\.;]/,r=n.exec(t)!=null;return r&&typeof encodeURIComponent!=e?encodeURIComponent(t):t}var e="undefined",t="object",n="Shockwave Flash",r="ShockwaveFlash.ShockwaveFlash",i="application/x-shockwave-flash",s="SWFObjectExprInst",o="onreadystatechange",u=window,a=document,f=navigator,l=!1,c=[A],h=[],p=[],d=[],v,m,g,y,b=!1,w=!1,E,S,x=!0,T=function(){var s=typeof a.getElementById!=e&&typeof a.getElementsByTagName!=e&&typeof a.createElement!=e,o=f.userAgent.toLowerCase(),c=f.platform.toLowerCase(),h=c?/win/.test(c):/win/.test(o),p=c?/mac/.test(c):/mac/.test(o),d=/webkit/.test(o)?parseFloat(o.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,v=!1,m=[0,0,0],g=null;if(typeof f.plugins!=e&&typeof f.plugins[n]==t)g=f.plugins[n].description,g&&(typeof f.mimeTypes==e||!f.mimeTypes[i]||!!f.mimeTypes[i].enabledPlugin)&&(l=!0,v=!1,g=g.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),m[0]=parseInt(g.replace(/^(.*)\..*$/,"$1"),10),m[1]=parseInt(g.replace(/^.*\.(.*)\s.*$/,"$1"),10),m[2]=/[a-zA-Z]/.test(g)?parseInt(g.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0);else if(typeof u[["Active"].concat("Object").join("X")]!=e)try{var y=new(window[["Active"].concat("Object").join("X")])(r);y&&(g=y.GetVariable("$version"),g&&(v=!0,g=g.split(" ")[1].split(","),m=[parseInt(g[0],10),parseInt(g[1],10),parseInt(g[2],10)]))}catch(b){}return{w3:s,pv:m,wk:d,ie:v,win:h,mac:p}}(),N=function(){if(!T.w3)return;(typeof a.readyState!=e&&a.readyState=="complete"||typeof a.readyState==e&&(a.getElementsByTagName("body")[0]||a.body))&&C(),b||(typeof a.addEventListener!=e&&a.addEventListener("DOMContentLoaded",C,!1),T.ie&&T.win&&(a.attachEvent(o,function(){a.readyState=="complete"&&(a.detachEvent(o,arguments.callee),C())}),u==top&&function(){if(b)return;try{a.documentElement.doScroll("left")}catch(e){setTimeout(arguments.callee,0);return}C()}()),T.wk&&function(){if(b)return;if(!/loaded|complete/.test(a.readyState)){setTimeout(arguments.callee,0);return}C()}(),L(C))}(),J=function(){T.ie&&T.win&&window.attachEvent("onunload",function(){var e=d.length;for(var t=0;t= 10.0.0 is required.");return}location.protocol=="file:"&&e.error("WARNING: web-socket-js doesn't work in file:///... URL unless you set Flash Security Settings properly. Open the page via Web server i.e. http://..."),WebSocket=function(e,t,n,r,i){var s=this;s.__id=WebSocket.__nextId++,WebSocket.__instances[s.__id]=s,s.readyState=WebSocket.CONNECTING,s.bufferedAmount=0,s.__events={},t?typeof t=="string"&&(t=[t]):t=[],setTimeout(function(){WebSocket.__addTask(function(){WebSocket.__flash.create(s.__id,e,t,n||null,r||0,i||null)})},0)},WebSocket.prototype.send=function(e){if(this.readyState==WebSocket.CONNECTING)throw"INVALID_STATE_ERR: Web Socket connection has not been established";var t=WebSocket.__flash.send(this.__id,encodeURIComponent(e));return t<0?!0:(this.bufferedAmount+=t,!1)},WebSocket.prototype.close=function(){if(this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING)return;this.readyState=WebSocket.CLOSING,WebSocket.__flash.close(this.__id)},WebSocket.prototype.addEventListener=function(e,t,n){e in this.__events||(this.__events[e]=[]),this.__events[e].push(t)},WebSocket.prototype.removeEventListener=function(e,t,n){if(!(e in this.__events))return;var r=this.__events[e];for(var i=r.length-1;i>=0;--i)if(r[i]===t){r.splice(i,1);break}},WebSocket.prototype.dispatchEvent=function(e){var t=this.__events[e.type]||[];for(var n=0;n"),this.doc.close(),this.doc.parentWindow.s=this;var e=this.doc.createElement("div");e.className="socketio",this.doc.body.appendChild(e),this.iframe=this.doc.createElement("iframe"),e.appendChild(this.iframe);var n=this,r=t.util.query(this.socket.options.query,"t="+ +(new Date));this.iframe.src=this.prepareUrl()+r,t.util.on(window,"unload",function(){n.destroy()})},n.prototype._=function(e,t){this.onData(e);try{var n=t.getElementsByTagName("script")[0];n.parentNode.removeChild(n)}catch(r){}},n.prototype.destroy=function(){if(this.iframe){try{this.iframe.src="about:blank"}catch(e){}this.doc=null,this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,CollectGarbage()}},n.prototype.close=function(){return this.destroy(),t.Transport.XHR.prototype.close.call(this)},n.check=function(e){if(typeof window!="undefined"&&["Active"].concat("Object").join("X")in window)try{var n=new(window[["Active"].concat("Object").join("X")])("htmlfile");return n&&t.Transport.XHR.check(e)}catch(r){}return!1},n.xdomainCheck=function(){return!1},t.transports.push("htmlfile")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(e,t,n){function r(){t.Transport.XHR.apply(this,arguments)}function i(){}e["xhr-polling"]=r,t.util.inherit(r,t.Transport.XHR),t.util.merge(r,t.Transport.XHR),r.prototype.name="xhr-polling",r.prototype.heartbeats=function(){return!1},r.prototype.open=function(){var e=this;return t.Transport.XHR.prototype.open.call(e),!1},r.prototype.get=function(){function t(){this.readyState==4&&(this.onreadystatechange=i,this.status==200?(e.onData(this.responseText),e.get()):e.onClose())}function r(){this.onload=i,this.onerror=i,e.retryCounter=1,e.onData(this.responseText),e.get()}function s(){e.retryCounter++,!e.retryCounter||e.retryCounter>3?e.onClose():e.get()}if(!this.isOpen)return;var e=this;this.xhr=this.request(),n.XDomainRequest&&this.xhr instanceof XDomainRequest?(this.xhr.onload=r,this.xhr.onerror=s):this.xhr.onreadystatechange=t,this.xhr.send(null)},r.prototype.onClose=function(){t.Transport.XHR.prototype.onClose.call(this);if(this.xhr){this.xhr.onreadystatechange=this.xhr.onload=this.xhr.onerror=i;try{this.xhr.abort()}catch(e){}this.xhr=null}},r.prototype.ready=function(e,n){var r=this;t.util.defer(function(){n.call(r)})},t.transports.push("xhr-polling")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),function(e,t,n){function i(e){t.Transport["xhr-polling"].apply(this,arguments),this.index=t.j.length;var n=this;t.j.push(function(e){n._(e)})}var r=n.document&&"MozAppearance"in n.document.documentElement.style;e["jsonp-polling"]=i,t.util.inherit(i,t.Transport["xhr-polling"]),i.prototype.name="jsonp-polling",i.prototype.post=function(e){function a(){f(),n.socket.setBuffer(!1)}function f(){n.iframe&&n.form.removeChild(n.iframe);try{u=document.createElement('