├── 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 | [](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 |
44 |
45 |
46 |
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='",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