├── README.md ├── access_point_if_no_wifi ├── arduino_sketch ├── .gitignore ├── ino.ini ├── lib │ └── .holder └── src │ └── sketch.ino ├── audio ├── .mkdir ├── busy.mp3 ├── button.mp3 ├── close_the_door.mp3 ├── finished.mp3 ├── not_found.mp3 ├── ok1.mp3 ├── ok2.mp3 ├── ok3.mp3 ├── ready1.mp3 ├── ready2.mp3 ├── ready3.mp3 ├── ready4.mp3 ├── seriously.mp3 ├── start.mp3 ├── startup.mp3 ├── stir.mp3 └── stop.mp3 ├── barcode_instructions ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── barcode_instructions.rb ├── bin │ └── mw_barcode_instructions ├── config.yml.example └── lib │ ├── microwave.rb │ ├── microwave │ ├── barcode_scanner.rb │ └── cooking_step.rb │ └── microwave_cooking_db.rb ├── clock └── bin │ └── mw_set_clock ├── init_scripts ├── mw_access_point ├── mw_barcode_instructions ├── mw_daemon ├── mw_set_clock ├── mw_startup_sound ├── mw_voice_control ├── mw_web_app └── piglow_cpu ├── microwave_daemon ├── .gitignore ├── bin │ └── mw_daemon ├── daemon.rb ├── ext │ └── microwave │ │ ├── extconf.rb │ │ └── microwave.c ├── lib │ ├── audio_player.rb │ ├── client.rb │ └── serial_microwave.rb └── mwtest.rb ├── scripts └── setup_audio ├── setup_bin_and_init ├── sinatra_app ├── .bundle │ └── config ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── bin │ └── mw_web_app ├── microwave_webapp.rb ├── public │ ├── Raspberry_Pi_Logo.png │ ├── application.css │ ├── application.js │ ├── jquery-1.10.1.min.js │ └── touchpad-background.jpg └── views │ └── touchpad.erb ├── uart_test ├── .bundle │ └── config ├── Gemfile ├── Gemfile.lock └── uart_test.rb └── voice_control ├── .gitignore ├── bin └── mw_voice_control ├── corpus.txt ├── install_pocketsphinx.sh ├── lmtool ├── 4906.dic ├── 4906.lm ├── 4906.log_pronounce ├── 4906.sent ├── 4906.vocab └── TAR4906.tgz ├── pocketsphinx_microwave.c └── voice_control.rb /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi-crowave 2 | 3 | I put a Raspberry Pi in my microwave. 4 | 5 | Now my microwave has: 6 | 7 | * Re-designed UI 8 | * Nicer sound effects 9 | * Clock is automatically updated from the internet 10 | * Voice control 11 | * Barcode-scanner to look up cooking instructions 12 | * Online database of cooking instructions at http://www.microwavecookingdb.com 13 | * Web page so you can control the microwave from your phone, and set up cooking instructions for products 14 | * Tweets after finished cooking 15 | -------------------------------------------------------------------------------- /access_point_if_no_wifi: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Interface checker 4 | # Checks to see whether interface has an IP address, if it doesn't assume it's down and start hostapd 5 | # Author : SirLagz 6 | # 7 | Interface='wlan0' 8 | HostAPDIP='192.168.42.1' 9 | echo "-----------------------------------" 10 | echo "Checking connectivity of $Interface" 11 | NetworkUp=`/sbin/ifconfig $Interface` 12 | IP=`echo "$NetworkUp" | grep inet | wc -l` 13 | if [[ $IP -eq 0 ]]; then 14 | echo "Connection is down" 15 | hostapd=`pidof hostapd` 16 | if [[ -z $hostapd ]]; then 17 | # If there are any more actions required when the interface goes down, add them here 18 | echo "Attempting to start hostapd" 19 | /etc/init.d/hostapd start 20 | echo "Attempting to start dnsmasq" 21 | /etc/init.d/dnsmasq start 22 | echo "Setting IP Address for wlan0" 23 | /sbin/ifconfig wlan0 $HostAPDIP netmask 255.255.255.0 up 24 | fi 25 | elif [[ $IP -eq 1 && $NetworkUp =~ $HostAPDIP ]]; then 26 | echo "IP is $HostAPDIP - hostapd is running" 27 | else 28 | echo "Connection is up" 29 | hostapd=`pidof hostapd` 30 | if [[ ! -z $hostapd ]]; then 31 | echo "Attempting to stop hostapd" 32 | /etc/init.d/hostapd stop 33 | echo "Attempting to stop dnsmasq" 34 | /etc/init.d/udhcpd stop 35 | echo "Renewing IP Address for $Interface" 36 | /sbin/dhclient wlan0 37 | fi 38 | fi 39 | echo "-----------------------------------" 40 | -------------------------------------------------------------------------------- /arduino_sketch/.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | -------------------------------------------------------------------------------- /arduino_sketch/ino.ini: -------------------------------------------------------------------------------- 1 | [build] 2 | board-model = nano328 3 | 4 | [upload] 5 | board-model = nano328 -------------------------------------------------------------------------------- /arduino_sketch/lib/.holder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/arduino_sketch/lib/.holder -------------------------------------------------------------------------------- /arduino_sketch/src/sketch.ino: -------------------------------------------------------------------------------- 1 | #define DEBUG false 2 | #define SERIAL_TIMEOUT 1000 3 | #define VOICE_COMMAND_TIMEOUT 10000 // Allow voice commands up to 10 seconds after the door closes 4 | 5 | #define scanDataPin 2 6 | #define scanLatchPin 3 7 | #define scanClockPin 4 8 | 9 | #define inputDataPin 7 10 | #define inputLatchPin 6 11 | #define inputClockPin 5 12 | 13 | #define outputDataPin 8 14 | #define outputLatchPin 9 15 | #define outputClockPin 10 16 | 17 | #define btn0 0 18 | #define btn1 1 19 | #define btn2 2 20 | #define btn3 3 21 | #define btn4 4 22 | #define btn5 5 23 | #define btn6 6 24 | #define btn7 7 25 | #define btn8 8 26 | #define btn9 9 27 | #define btnWeightDefrost 10 28 | #define btnJetDefrost 11 29 | #define btnPreset 12 30 | #define btnTime 13 31 | #define btnExpress 14 32 | #define btnCancel 15 33 | #define btnPower 16 34 | #define btnMemory 17 35 | #define btnClock 18 36 | #define btnStart 19 37 | 38 | #define newBtnHigh10s 1 39 | #define newBtnHigh20s 2 40 | #define newBtnHigh30s 3 41 | #define newBtnHigh1m 4 42 | #define newBtnHigh2m 5 43 | #define newBtnMed10s 6 44 | #define newBtnMed20s 7 45 | #define newBtnMed30s 8 46 | #define newBtnMed1m 9 47 | #define newBtnMed2m 0 48 | #define newBtnStart 10 49 | #define newBtnStop 11 50 | #define newBtn10s 12 51 | #define newBtn10m 13 52 | #define newBtnMed 14 53 | #define newBtnDefrost 15 54 | #define newBtn1s 16 55 | #define newBtn1m 17 56 | #define newBtnHigh 18 57 | #define newBtnLow 19 58 | 59 | #define doorSwitch B01000000 60 | #define buttonMask B00111111 61 | 62 | const int MAX_SECONDS = 5999; // (99 * 60) + 59 63 | const int POWER_HIGH = 10; 64 | const int POWER_MEDIUM = 7; 65 | const int POWER_LOW = 5; 66 | const int POWER_DEFROST = 3; 67 | const int POWER_ZERO = 0; 68 | 69 | // btnMatrix[VCC Pin][GND Pin] 70 | int btnMatrix[6][6] = { 71 | {-1, -1, -1, -1, -1, 19}, 72 | {-1, 0, 1, 2, 3, -1}, 73 | {-1, 4, 5, 6, 7, -1}, 74 | {10, 8, 9, 12, 15, -1}, 75 | {-1, 13, 18, 11, 16, -1}, 76 | {17, -1, 14, -1, -1, -1} 77 | }; 78 | 79 | char btnNames[][15] = { 80 | "0", 81 | "1", 82 | "2", 83 | "3", 84 | "4", 85 | "5", 86 | "6", 87 | "7", 88 | "8", 89 | "9", 90 | "Weight Defrost", 91 | "Jet Defrost", 92 | "Pre-set", 93 | "Time", 94 | "Express", 95 | "Cancel", 96 | "Power", 97 | "Memory", 98 | "Clock", 99 | "Start" 100 | }; 101 | 102 | byte inputByte, buttonByte, scanMask; 103 | int currentButton = -1; 104 | int lastButton = -1; 105 | int lastPushedButton = -1; 106 | bool on = false; 107 | bool paused = false; 108 | bool doorOpen = false; 109 | bool pendingStart = false; 110 | bool commandFromVoice = false; 111 | int currentPower = 0; 112 | int currentTime = 0; 113 | 114 | char serCommand, serParams; 115 | char infoString[18]; 116 | 117 | unsigned long serialTimer, buttonTimer, countdownTimer, doorTimer; 118 | 119 | void setup() { 120 | //start serial 121 | Serial.begin(9600); 122 | 123 | //define pin modes 124 | pinMode(scanDataPin, OUTPUT); 125 | pinMode(scanLatchPin, OUTPUT); 126 | pinMode(scanClockPin, OUTPUT); 127 | 128 | pinMode(inputDataPin, INPUT); 129 | pinMode(inputLatchPin, OUTPUT); 130 | pinMode(inputClockPin, OUTPUT); 131 | 132 | pinMode(outputDataPin, OUTPUT); 133 | pinMode(outputLatchPin, OUTPUT); 134 | pinMode(outputClockPin, OUTPUT); 135 | } 136 | 137 | void loop() { 138 | commandFromVoice = false; 139 | 140 | while (Serial.available() > 0) { 141 | /* Serial API 142 | * 143 | * To start microwave for 2 minutes on high, send the following commands: plht120;s 144 | * (power level high, time 120s, start) 145 | * 146 | */ 147 | serCommand = Serial.read(); 148 | switch (serCommand) { 149 | // Voice - indicates that the following commands come from speech recognition without keywords. 150 | // Only run the commands if the microwave door was recently closed 151 | case 'v': 152 | if (DEBUG) { Serial.println("Voice command!"); } 153 | commandFromVoice = true; 154 | break; 155 | 156 | // Button | bc => Push Cancel button 157 | case 'b': 158 | if (waitForSerial()) { 159 | serParams = Serial.read(); 160 | switch (serParams) { 161 | case '0': pushButton(btn0); break; 162 | case '1': pushButton(btn1); break; 163 | case '2': pushButton(btn2); break; 164 | case '3': pushButton(btn3); break; 165 | case '4': pushButton(btn4); break; 166 | case '5': pushButton(btn5); break; 167 | case '6': pushButton(btn6); break; 168 | case '7': pushButton(btn7); break; 169 | case '8': pushButton(btn8); break; 170 | case '9': pushButton(btn9); break; 171 | case 'w': pushButton(btnWeightDefrost); break; 172 | case 'j': pushButton(btnJetDefrost); break; 173 | case 'r': pushButton(btnPreset); break; 174 | case 't': pushButton(btnTime); break; 175 | case 'e': pushButton(btnExpress); break; 176 | case 'c': pushButton(btnCancel); break; 177 | case 'p': pushButton(btnPower); break; 178 | case 'm': pushButton(btnMemory); break; 179 | case 'l': pushButton(btnClock); break; 180 | case 's': pushButton(btnStart); break; 181 | } 182 | } 183 | break; 184 | 185 | case 'i': 186 | // Return info about microwave status 187 | sprintf(infoString, "%d;%d;%d;%d;%d", 188 | on ? 1 : 0, 189 | paused ? 1 : 0, 190 | doorOpen ? 1 : 0, 191 | currentPower, 192 | currentTime 193 | ); 194 | Serial.println(infoString); 195 | break; 196 | 197 | // Clock | c1135 => Set clock to 11:35 198 | case 'c': 199 | if (waitForSerial()) { 200 | setClock(Serial.parseInt()); 201 | } 202 | break; 203 | 204 | // Time | t90; => Set time to 90 seconds 205 | case 't': 206 | if (waitForSerial() && voiceCommandAllowed()) { 207 | setTime(Serial.parseInt()); 208 | } 209 | break; 210 | 211 | // Power (integer or level) 212 | // Integer: pi6; => Set power to 6 213 | // Level: plh => Set power to high (10) 214 | case 'p': 215 | if (waitForSerial() && voiceCommandAllowed()) { 216 | serParams = Serial.read(); 217 | 218 | if (serParams == 'i') { 219 | // Power integer 220 | setPower(Serial.parseInt()); 221 | 222 | } else if (serParams == 'l') { 223 | // Power level 224 | if (waitForSerial()) { 225 | serParams = Serial.read(); 226 | switch (serParams) { 227 | case 'h': setPower(POWER_HIGH); break; 228 | case 'm': setPower(POWER_MEDIUM); break; 229 | case 'l': setPower(POWER_LOW); break; 230 | case 'd': setPower(POWER_DEFROST); break; 231 | case 'o': setPower(POWER_ZERO); break; 232 | } 233 | } 234 | } 235 | } 236 | break; 237 | 238 | // Start 239 | case 's': 240 | if (voiceCommandAllowed()) { 241 | start(); 242 | } 243 | break; 244 | 245 | // Stop 246 | case 'S': 247 | if (voiceCommandAllowed()) { 248 | stop(); 249 | } 250 | break; 251 | 252 | // Pause 253 | case 'P': 254 | pause(); 255 | break; 256 | } 257 | if (commandFromVoice) { 258 | if (! waitForSerial()) { 259 | if (DEBUG) { Serial.println("Voice command over."); } 260 | } 261 | } 262 | } 263 | 264 | inputByte = 0; 265 | 266 | for (scanMask = B00000010; scanMask <= B01000000; scanMask <<= 1) { 267 | digitalWrite(scanLatchPin, LOW); 268 | shiftOut(scanDataPin, scanClockPin, MSBFIRST, scanMask); 269 | digitalWrite(scanLatchPin, HIGH); 270 | 271 | // Ignore first bit (floating) 272 | inputByte = shiftIn(inputDataPin, inputClockPin, inputLatchPin) & B01111111; 273 | 274 | // 1 = closed, 0 = open 275 | if (!(inputByte & doorSwitch)) { 276 | // Stop if door is opened 277 | if (on) { stop(); } 278 | if (!doorOpen) { 279 | if (DEBUG) { Serial.println("Door is now open."); } 280 | doorOpen = true; 281 | } 282 | } else { 283 | // Set door state if door is now closed 284 | if (doorOpen) { 285 | if (DEBUG) { Serial.println("Door is now closed."); } 286 | doorOpen = false; 287 | doorTimer = millis(); // Allow voice commands up to 7 seconds after door was closed 288 | 289 | // Check to see if start was pressed while door was open 290 | // If so, start the microwave now. 291 | if (pendingStart) { start(); } 292 | } 293 | } 294 | 295 | buttonByte = inputByte & buttonMask; 296 | 297 | if (buttonByte != 0) { 298 | currentButton = pressedButton(scanMask, buttonByte); 299 | 300 | if (currentButton != -1 && currentButton != lastButton) { 301 | switch (currentButton) { 302 | case newBtnHigh10s: quickStart(10, POWER_HIGH); break; 303 | case newBtnHigh20s: quickStart(20, POWER_HIGH); break; 304 | case newBtnHigh30s: quickStart(30, POWER_HIGH); break; 305 | case newBtnHigh1m: quickStart(60, POWER_HIGH); break; 306 | case newBtnHigh2m: quickStart(120, POWER_HIGH); break; 307 | case newBtnMed10s: quickStart(10, POWER_MEDIUM); break; 308 | case newBtnMed20s: quickStart(20, POWER_MEDIUM); break; 309 | case newBtnMed30s: quickStart(30, POWER_MEDIUM); break; 310 | case newBtnMed1m: quickStart(60, POWER_MEDIUM); break; 311 | case newBtnMed2m: quickStart(120, POWER_MEDIUM); break; 312 | case newBtn10s: incrementTime(10); break; 313 | case newBtn10m: incrementTime(600); break; 314 | case newBtn1s: incrementTime(1); break; 315 | case newBtn1m: incrementTime(60); break; 316 | // Can't set power while microwave is on 317 | case newBtnHigh: if (!on) { setPower(POWER_HIGH); }; break; 318 | case newBtnMed: if (!on) { setPower(POWER_MEDIUM); }; break; 319 | case newBtnLow: if (!on) { setPower(POWER_LOW); }; break; 320 | case newBtnDefrost: if (!on) { setPower(POWER_DEFROST); }; break; 321 | case newBtnStart: start(); break; 322 | case newBtnStop: stop(); break; 323 | } 324 | 325 | lastButton = currentButton; 326 | } 327 | 328 | buttonTimer = millis(); 329 | } else { 330 | // Debounce buttons with 100ms delay 331 | if (lastButton != -1 && millis() - 100 > buttonTimer) { 332 | lastButton = -1; 333 | buttonTimer = 0; 334 | } 335 | } 336 | } 337 | 338 | if (on) { 339 | // Decrement time counter every 1000ms 340 | if (millis() - 1000 > countdownTimer) { 341 | countdownTimer = millis(); 342 | currentTime -= 1; 343 | if (currentTime <= 0) { 344 | stop(); 345 | } 346 | } 347 | } 348 | } 349 | 350 | bool waitForSerial() { 351 | serialTimer = millis(); 352 | while(!Serial.available()) { 353 | if (millis() - serialTimer > SERIAL_TIMEOUT) { 354 | return false; 355 | } 356 | } 357 | return true; 358 | } 359 | 360 | // Check voice commands 361 | bool voiceCommandAllowed() { 362 | if (commandFromVoice) { 363 | return millis() - VOICE_COMMAND_TIMEOUT < doorTimer; 364 | } 365 | return true; 366 | } 367 | 368 | byte shiftIn(int dataPin, int clockPin, int latchPin) { 369 | int i; 370 | int tmp = 0; 371 | byte dataIn = 0; 372 | 373 | digitalWrite(latchPin, HIGH); 374 | delayMicroseconds(20); 375 | digitalWrite(latchPin, LOW); 376 | 377 | for (i = 7; i >= 0; i--) { 378 | digitalWrite(clockPin, LOW); 379 | delayMicroseconds(2); 380 | tmp = digitalRead(dataPin); 381 | if (tmp) { 382 | dataIn = dataIn | (1 << i); 383 | } 384 | digitalWrite(clockPin, HIGH); 385 | } 386 | 387 | return dataIn; 388 | } 389 | 390 | // If multiple buttons are pressed, this 391 | // function just returns the first detected button. 392 | // Stop pressing so many buttons at once. 393 | int pressedButton(byte vccByte, byte gndByte) { 394 | int vcc, gnd, button; 395 | 396 | for (vcc = 0; vcc < 6; vcc++) { 397 | if (vccByte & (B01000000 >> vcc)) { 398 | for (gnd = 0; gnd < 6; gnd++) { 399 | if (gndByte & (B00100000 >> gnd)) { 400 | button = btnMatrix[vcc][gnd]; 401 | if (button != -1) { 402 | return button; 403 | } 404 | } 405 | } 406 | } 407 | } 408 | 409 | return -1; 410 | } 411 | 412 | void pushButton(int button) { 413 | if (DEBUG) { 414 | Serial.print("Pressing: "); 415 | Serial.println(btnNames[button]); 416 | } 417 | 418 | int buttonPins[2]; 419 | byte vccByte, gndByte; 420 | 421 | if (findButton(button, buttonPins)) { 422 | // VCC 1,2,3,4,5,6 = 7,6,5,4,3,2 (second register) 423 | // GND 1,2,3,4,5,6 = 7,6,5,4,3,2 (first register) 424 | vccByte = B01000000 >> buttonPins[0]; 425 | gndByte = B01000000 >> buttonPins[1]; 426 | 427 | // Only need a 'off' delay when pushing the same button twice 428 | if (lastPushedButton == button) { delay(25); } 429 | 430 | digitalWrite(outputLatchPin, LOW); 431 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, vccByte); 432 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, gndByte); 433 | digitalWrite(outputLatchPin, HIGH); 434 | 435 | // Start button needs a longer press 436 | delay(button == btnStart ? 64 : 25); 437 | 438 | digitalWrite(outputLatchPin, LOW); 439 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, 0); 440 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, 0); 441 | digitalWrite(outputLatchPin, HIGH); 442 | 443 | lastPushedButton = button; 444 | } 445 | } 446 | 447 | // Find button in matrix 448 | bool findButton(int button, int pins[]) { 449 | int vcc, gnd; 450 | for (vcc = 0; vcc < 6; vcc++) { 451 | for (gnd = 0; gnd < 6; gnd++) { 452 | if (btnMatrix[vcc][gnd] == button) { 453 | pins[0] = vcc; 454 | pins[1] = gnd; 455 | return true; 456 | } 457 | } 458 | } 459 | return false; 460 | } 461 | 462 | void setTime(int totalSeconds) { 463 | if (totalSeconds > MAX_SECONDS) { return; } 464 | 465 | // Setting time will stop the microwave 466 | if (on) { on = false; } 467 | 468 | int minutes, seconds; 469 | currentTime = totalSeconds; 470 | 471 | pushButton(btnTime); 472 | 473 | minutes = totalSeconds / 60; 474 | seconds = totalSeconds % 60; 475 | 476 | if (minutes != 0) { 477 | if (minutes / 10 != 0) { 478 | pushButton(minutes / 10); 479 | } 480 | pushButton(minutes % 10); 481 | } 482 | if (seconds >= 10 || minutes != 0) { 483 | pushButton(seconds / 10); 484 | } 485 | pushButton(seconds % 10); 486 | } 487 | 488 | void incrementTime(int extraSeconds) { 489 | if (currentTime + extraSeconds > MAX_SECONDS) { currentTime = 0; } 490 | setTime(currentTime + extraSeconds); 491 | } 492 | 493 | void setPower(int power) { 494 | if (power < 0 || power > 10) { return; } 495 | 496 | currentPower = power; 497 | 498 | pushButton(btnPower); 499 | 500 | if (power == 10) { 501 | pushButton(1); 502 | pushButton(0); 503 | } else { 504 | pushButton(power); 505 | } 506 | } 507 | 508 | // time is an int, of the form 'hhmm', without leading zeros. 509 | // Times must be between 1:00 and 12:59 510 | void setClock(unsigned int time) { 511 | if (time > 1259 || time < 100 || time % 100 > 59 ) { return; } 512 | 513 | pushButton(btnClock); 514 | pushButton(btnCancel); 515 | 516 | if (time >= 1000) { pushButton(1); } 517 | pushButton(time % 1000 / 100); 518 | pushButton(time % 100 / 10); 519 | pushButton(time % 10); 520 | 521 | pushButton(btnClock); 522 | } 523 | 524 | void start() { 525 | // Only start if door is closed 526 | if (!doorOpen) { 527 | if (currentTime > 0) { 528 | pushButton(btnStart); 529 | on = true; 530 | paused = false; 531 | pendingStart = false; 532 | // Set door timer to 0, so that no further commands are run 533 | doorTimer = 0; 534 | // Set countdown timer to decrement time remaining 535 | countdownTimer = millis(); 536 | } 537 | } else { 538 | // Record the fact that start was pushed while the door was open. 539 | // Once door is closed, start the microwave. 540 | pendingStart = true; 541 | } 542 | } 543 | 544 | void stop() { 545 | pushButton(btnCancel); 546 | on = false; 547 | paused = false; 548 | pendingStart = false; 549 | currentTime = 0; 550 | currentPower = 0; 551 | } 552 | 553 | void pause() { 554 | // Can only pause when microwave is on 555 | if (on) { 556 | pushButton(btnTime); 557 | on = false; 558 | paused = true; 559 | } 560 | } 561 | 562 | void quickStart(int seconds, int power) { 563 | setTime(seconds); 564 | setPower(power); 565 | start(); 566 | } 567 | 568 | // void printBinary(byte var) { 569 | // for (byte mask = B10000000; mask; mask >>= 1) { 570 | // Serial.print(mask & var ? '1' : '0'); 571 | // } 572 | // Serial.println(); 573 | // } 574 | -------------------------------------------------------------------------------- /audio/.mkdir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/.mkdir -------------------------------------------------------------------------------- /audio/busy.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/busy.mp3 -------------------------------------------------------------------------------- /audio/button.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/button.mp3 -------------------------------------------------------------------------------- /audio/close_the_door.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/close_the_door.mp3 -------------------------------------------------------------------------------- /audio/finished.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/finished.mp3 -------------------------------------------------------------------------------- /audio/not_found.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/not_found.mp3 -------------------------------------------------------------------------------- /audio/ok1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ok1.mp3 -------------------------------------------------------------------------------- /audio/ok2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ok2.mp3 -------------------------------------------------------------------------------- /audio/ok3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ok3.mp3 -------------------------------------------------------------------------------- /audio/ready1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ready1.mp3 -------------------------------------------------------------------------------- /audio/ready2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ready2.mp3 -------------------------------------------------------------------------------- /audio/ready3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ready3.mp3 -------------------------------------------------------------------------------- /audio/ready4.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/ready4.mp3 -------------------------------------------------------------------------------- /audio/seriously.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/seriously.mp3 -------------------------------------------------------------------------------- /audio/start.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/start.mp3 -------------------------------------------------------------------------------- /audio/startup.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/startup.mp3 -------------------------------------------------------------------------------- /audio/stir.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/stir.mp3 -------------------------------------------------------------------------------- /audio/stop.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/audio/stop.mp3 -------------------------------------------------------------------------------- /barcode_instructions/.gitignore: -------------------------------------------------------------------------------- 1 | config.yml -------------------------------------------------------------------------------- /barcode_instructions/Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | gem 'bundler', '>= 1.2.0' 3 | 4 | gem 'httparty' 5 | gem 'confstruct' 6 | gem 'barby' 7 | gem 'libdevinput' 8 | gem 'twitter' 9 | 10 | gem 'debugger' -------------------------------------------------------------------------------- /barcode_instructions/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | barby (0.5.1) 5 | columnize (0.3.6) 6 | confstruct (0.2.5) 7 | debugger (1.6.0) 8 | columnize (>= 0.3.1) 9 | debugger-linecache (~> 1.2.0) 10 | debugger-ruby_core_source (~> 1.2.1) 11 | debugger-linecache (1.2.0) 12 | debugger-ruby_core_source (1.2.3) 13 | faraday (0.8.7) 14 | multipart-post (~> 1.1) 15 | httparty (0.11.0) 16 | multi_json (~> 1.0) 17 | multi_xml (>= 0.5.2) 18 | libdevinput (0.0.1) 19 | multi_json (1.7.7) 20 | multi_xml (0.5.4) 21 | multipart-post (1.2.0) 22 | simple_oauth (0.2.0) 23 | twitter (4.8.1) 24 | faraday (~> 0.8, < 0.10) 25 | multi_json (~> 1.0) 26 | simple_oauth (~> 0.2) 27 | 28 | PLATFORMS 29 | ruby 30 | 31 | DEPENDENCIES 32 | barby 33 | bundler (>= 1.2.0) 34 | confstruct 35 | debugger 36 | httparty 37 | libdevinput 38 | twitter 39 | -------------------------------------------------------------------------------- /barcode_instructions/barcode_instructions.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift File.join(File.dirname(__FILE__), 'lib') 3 | 4 | BARCODE_SCANNER_EVENT = "/dev/input/by-id/usb-040b_6543-event-kbd" 5 | ARDUINO_DAEMON_PORT = 3141 6 | 7 | require 'rubygems' 8 | require 'yaml' 9 | require 'thread' 10 | require 'microwave' 11 | require 'microwave_cooking_db' 12 | require 'confstruct' 13 | require 'twitter' 14 | 15 | Config = Confstruct::Configuration.new(YAML.load_file(File.expand_path("../config.yml", __FILE__))) 16 | 17 | Twitter.configure do |config| 18 | config.consumer_key = Config.twitter.consumer_key 19 | config.consumer_secret = Config.twitter.consumer_secret 20 | config.oauth_token = Config.twitter.oauth_token 21 | config.oauth_token_secret = Config.twitter.oauth_token_secret 22 | end 23 | 24 | connected = false 25 | until connected 26 | begin 27 | @microwave = Microwave.new 28 | connected = true 29 | rescue Exception => ex 30 | p $!, *$@ 31 | sleep 2 32 | end 33 | end 34 | 35 | @microwave.start_thread!(:fetch_microwave_info) 36 | @microwave.start_thread!(:fetch_barcodes) 37 | @microwave.start_thread!(:process_barcodes).join 38 | -------------------------------------------------------------------------------- /barcode_instructions/bin/mw_barcode_instructions: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /home/ndbroadbent/raspberry_picrowave/barcode_instructions 3 | ./barcode_instructions.rb -------------------------------------------------------------------------------- /barcode_instructions/config.yml.example: -------------------------------------------------------------------------------- 1 | microwave_power: 2 | 3 | mwcdb: # Microwave Cooking Database 4 | api_key: 5 | email: 6 | 7 | twitter: 8 | consumer_key: 9 | consumer_secret: 10 | oauth_token: 11 | oauth_token_secret -------------------------------------------------------------------------------- /barcode_instructions/lib/microwave.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../../microwave_daemon/lib/client', __FILE__) 2 | require 'microwave/barcode_scanner' 3 | require 'microwave/cooking_step' 4 | 5 | class Microwave 6 | TWEET_PREFIXES = [ 7 | "Just finished cooking up some", 8 | "Just cooked some", 9 | "Just finished cooking some", 10 | "Just heated up some", 11 | "Finished cooking" 12 | ] 13 | 14 | TWEET_SUFFIXES = [ 15 | "I bet it tastes pretty good!", 16 | "I would totally eat it if I had a mouth.", 17 | "I'm pretty sure it tastes like chicken. Or maybe tasty wheat.", 18 | "I hope they like it!", 19 | "I cooked it with my microwaves.", 20 | "Mmmmmm, tastes like 2.45 GHz non-ionizing radiation.", 21 | "" 22 | ] 23 | 24 | attr_accessor :arduino, :barcode_scanner 25 | 26 | def initialize 27 | @microwave = Daemon::Client.new 28 | @barcode_scanner = BarcodeScanner.new 29 | @product_queue = Queue.new 30 | 31 | @mwcdb_client = MicrowaveCookingDB.new( 32 | email: Config.mwcdb.email, api_key: Config.mwcdb.api_key, power: Config.microwave_power) 33 | end 34 | 35 | def start_thread!(method) 36 | Thread.new do 37 | while true 38 | begin 39 | self.send(method) 40 | rescue Exception => ex 41 | puts "Error from #{method}!" 42 | p $!, *$@ 43 | sleep 2 44 | end 45 | end 46 | end 47 | end 48 | 49 | def fetch_microwave_info 50 | begin 51 | last_info = nil 52 | 53 | while true 54 | @microwave.fetch_info 55 | 56 | if last_info != @microwave.info 57 | #puts @microwave.info.inspect 58 | last_info = @microwave.info 59 | end 60 | 61 | sleep 0.5 62 | end 63 | rescue Exception => ex 64 | # Try re-initializing until connection succeeds 65 | @microwave = Daemon::Client.new 66 | raise ex 67 | end 68 | end 69 | 70 | def fetch_barcodes 71 | @barcode_scanner.listen! do |upc| 72 | product = @mwcdb_client.find(upc) 73 | if product 74 | record_upc(:known, upc) 75 | @product_queue << product 76 | else 77 | record_upc(:unknown, upc) 78 | play "not_found" # Sorry, I don't know how to cook that! 79 | end 80 | end 81 | end 82 | 83 | def process_barcodes 84 | while true 85 | product = @product_queue.pop 86 | 87 | puts "Cooking: #{product['name']}" 88 | 89 | steps = CookingStep.steps_for_product(product) 90 | 91 | # Microwave will count down total time of all steps 92 | time_remaining = steps.map(&:time).inject(:+) 93 | 94 | # If barcode was scanned while door was open, wait for door to close 95 | 96 | # TODO - fix later 97 | 98 | # if @microwave.info[:door_open] 99 | # puts "Waiting for microwave door to close..." 100 | # sleep(0.5) until !@microwave.info[:door_open] 101 | # end 102 | 103 | steps.each do |step| 104 | play step.instruction if step.instruction 105 | 106 | if step.wait_for_door_cycle 107 | return false unless wait_for_door_cycle(step) 108 | 109 | elsif step.time > 0 110 | @microwave.start(time_remaining, step.power) 111 | sleep 1 # Give microwave info time to refresh before checking interrupt 112 | 113 | puts "Waiting #{step.time} seconds..." 114 | ((step.time - 1) * 2).times do 115 | sleep 0.5 116 | return false if check_for_interrupt 117 | end 118 | 119 | time_remaining -= step.time 120 | 121 | if time_remaining > 0 122 | puts "Time remaining: #{time_remaining}" 123 | 124 | @microwave.pause 125 | else 126 | # Finished! 127 | Twitter.update("#{TWEET_PREFIXES.sample} #{product["name"]}! #{TWEET_SUFFIXES.sample}") 128 | 129 | wait_for_food_to_be_taken 130 | end 131 | end 132 | end 133 | end 134 | end 135 | 136 | def wait_for_door_cycle(step) 137 | puts "Waiting for microwave door to open..." 138 | elapsed = 0 139 | until @microwave.info[:door_open] 140 | sleep(0.5) 141 | elapsed += 0.5 142 | 143 | if elapsed % 30 == 0 144 | play "seriously" # Seriously, what are you doing? 145 | end 146 | if elapsed % 15 == 0 147 | # Repeat instructions if there is a delay 148 | play step.instruction if step.instruction 149 | end 150 | 151 | return false if check_for_interrupt 152 | end 153 | 154 | puts "Waiting for microwave door to close..." 155 | elapsed = 0 156 | until !@microwave.info[:door_open] 157 | sleep(0.5) 158 | elapsed += 0.5 159 | 160 | if elapsed % 15 == 0 161 | play "close_the_door" # "Please close the door. I'm getting cold." 162 | end 163 | return false if check_for_interrupt 164 | end 165 | 166 | true 167 | end 168 | 169 | def wait_for_food_to_be_taken 170 | play "ready1" 171 | 172 | puts "Waiting for microwave door to open..." 173 | elapsed = 0 174 | stage = 2 175 | until @microwave.info[:door_open] 176 | sleep(0.5) 177 | elapsed += 0.5 178 | 179 | if elapsed == 25 180 | play "ready#{stage}" 181 | stage += 1 182 | stage = 2 if stage > 4 183 | elapsed = 0 184 | end 185 | end 186 | 187 | puts "Finished! Enjoy your food :)" 188 | end 189 | 190 | def check_for_interrupt 191 | if !@product_queue.empty? 192 | play "busy" # I'm busy! Please don't scan any more bar-codes. 193 | 194 | # Clear product_queue and keep the current program going. 195 | @product_queue.clear 196 | return false 197 | end 198 | 199 | # Interrupt the current cooking program if the microwave is off and not paused. 200 | if !@microwave.info[:on] && !@microwave.info[:paused] 201 | puts "Aborting cooking program! The microwave has been stopped." 202 | return true 203 | end 204 | end 205 | 206 | def play(file) 207 | puts "Playing: #{file}" 208 | path = File.expand_path("../../../audio/#{file}.mp3", __FILE__) 209 | `mpg123 "#{path}" > /dev/null 2>&1` 210 | end 211 | 212 | BARCODES_FILE = File.expand_path("../../../sinatra_app/recent_barcodes.yml", __FILE__) 213 | 214 | # Save the last 10 unique known and unknown upc barcodes to display in the Sinatra app 215 | def record_upc(key, upc) 216 | if File.exists?(BARCODES_FILE) 217 | barcodes = YAML.load_file(BARCODES_FILE) 218 | else 219 | barcodes = {known: [], unknown: []} 220 | end 221 | 222 | barcodes[key] << upc 223 | 224 | # Only store last 10 uniq barcodes 225 | [:known, :unknown].each do |key| 226 | barcodes[key] = barcodes[key].uniq[0,10] 227 | end 228 | 229 | File.open(BARCODES_FILE, 'w') do |f| 230 | f.puts barcodes.to_yaml 231 | end 232 | end 233 | end 234 | -------------------------------------------------------------------------------- /barcode_instructions/lib/microwave/barcode_scanner.rb: -------------------------------------------------------------------------------- 1 | require 'libdevinput' 2 | DevInput.class_eval { attr_reader :dev } 3 | EVIOCGRAB = 1074021776 4 | require 'barby/barcode/ean_13' 5 | require 'barby/barcode/upc_supplemental' 6 | 7 | class Microwave 8 | class BarcodeScanner 9 | def initialize 10 | @barcode_scanner = DevInput.new(BARCODE_SCANNER_EVENT) 11 | # Grab barcode scanner exclusively (so keypress events aren't heard by Linux) 12 | @barcode_scanner.dev.ioctl(EVIOCGRAB, 1) 13 | end 14 | 15 | def valid_barcode?(barcode) 16 | begin 17 | #Barby::EAN13.new(barcode[0...-1]).to_s == barcode || 18 | #Barby::UPCSupplemental.new(barcode[0...-1]).to_s == barcode 19 | # If you want to scan some shoes, you can scan some shoes. 20 | true 21 | rescue 22 | false 23 | end 24 | end 25 | 26 | def listen! 27 | puts "Ready for barcodes!" 28 | 29 | while true 30 | barcode = '' 31 | @barcode_scanner.each do |event| 32 | # Just listen for 'key presses' 33 | if event.type == 1 && event.value == 1 34 | if event.code == 28 # Enter key 35 | if valid_barcode?(barcode) 36 | yield barcode 37 | else 38 | puts "Not a UPC barcode! (#{barcode})" 39 | end 40 | barcode = '' 41 | else 42 | barcode << event.code_str 43 | end 44 | end 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /barcode_instructions/lib/microwave/cooking_step.rb: -------------------------------------------------------------------------------- 1 | class Microwave 2 | class CookingStep 3 | attr_accessor :time, :power, :instruction, :wait_for_door_cycle 4 | 5 | def initialize(step) 6 | @time = 0 7 | @power = 'off' 8 | 9 | case step["action"] 10 | when "Cook" 11 | @power = step['power'] if step['power'] 12 | @time = step['time'] if step['time'] 13 | 14 | when "Stand" 15 | @time = step['time'] if step['time'] 16 | 17 | when "Stir" 18 | @instruction = "stir" # Please open the microwave door and stir the food. 19 | @wait_for_door_cycle = true 20 | end 21 | end 22 | 23 | def self.steps_for_product(product) 24 | product['steps'].map do |step_params| 25 | new(step_params) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /barcode_instructions/lib/microwave_cooking_db.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | 3 | class MicrowaveCookingDB 4 | def initialize(options) 5 | @email = options[:email] 6 | @api_key = options[:api_key] 7 | @power = options[:power] 8 | end 9 | 10 | def find(upc) 11 | puts "Looking up #{upc} on microwavecookingdb.com..." 12 | 13 | # Retry failed request 3 times 14 | 3.times do 15 | begin 16 | product = HTTParty.get("http://www.microwavecookingdb.com/products/#{upc}.json", 17 | query: {email: @email, api_key: @api_key, power: @power}) 18 | return nil if product["error"] 19 | return product 20 | rescue Exception => ex 21 | p $!, *$@ 22 | nil 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /clock/bin/mw_set_clock: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Send clock command to the microwave daemon 3 | date +"{\"command\":{\"clock\":%-I%M}}" | netcat -q1 localhost 3141 -------------------------------------------------------------------------------- /init_scripts/mw_access_point: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_access_point 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_access_point 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Setup Microwave Access Point 11 | # Description: Setup Microwave Access Point if no Wifi connection 12 | ### END INIT INFO 13 | 14 | # Check wifi after a delay. If not connected, set up access point so we can configure wifi password 15 | sleep 20 16 | /home/ndbroadbent/raspberry_picrowave/access_point_if_no_wifi 17 | 18 | exit 0 -------------------------------------------------------------------------------- /init_scripts/mw_barcode_instructions: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_barcode_instructions 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_barcode_instructions 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Microwave Barcode Instructions 11 | # Description: Manage Microwave Barcode Instructions process 12 | ### END INIT INFO 13 | 14 | # Find PID of current process 15 | PID=$(ps aux | grep '[r]uby \./barcode_instructions\.rb' | awk '{print $2}' | head -n1) 16 | 17 | case "$1" in 18 | start) 19 | if [ -n "$PID" ]; then 20 | echo "mw_barcode_instructions is already running." 21 | else 22 | echo "Starting mw_barcode_instructions..." 23 | mw_barcode_instructions & 24 | fi 25 | ;; 26 | stop) 27 | if [ -n "$PID" ]; then 28 | echo "Stopping mw_barcode_instructions..." 29 | kill $PID 30 | else 31 | echo "mw_barcode_instructions is not running." 32 | fi 33 | ;; 34 | restart) 35 | $0 stop 36 | $0 start 37 | ;; 38 | *) 39 | echo "Usage: /etc/init.d/mw_barcode_instructions {start|stop|restart}" 40 | exit 1 41 | ;; 42 | esac 43 | 44 | exit 0 45 | -------------------------------------------------------------------------------- /init_scripts/mw_daemon: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_daemon 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_daemon 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Microwave Arduino Daemon 11 | # Description: Manage Microwave Arduino Daemon process 12 | ### END INIT INFO 13 | 14 | # Find PID of current process 15 | PID=$(ps aux | grep '[r]uby \./daemon\.rb' | awk '{print $2}' | head -n1) 16 | 17 | case "$1" in 18 | start) 19 | if [ -n "$PID" ]; then 20 | echo "mw_daemon is already running." 21 | else 22 | echo "Starting mw_daemon..." 23 | mw_daemon & 24 | fi 25 | ;; 26 | stop) 27 | if [ -n "$PID" ]; then 28 | echo "Stopping mw_daemon..." 29 | kill $PID 30 | else 31 | echo "mw_daemon is not running." 32 | fi 33 | ;; 34 | restart) 35 | $0 stop 36 | $0 start 37 | ;; 38 | *) 39 | echo "Usage: /etc/init.d/mw_daemon {start|stop|restart}" 40 | exit 1 41 | ;; 42 | esac 43 | 44 | exit 0 45 | -------------------------------------------------------------------------------- /init_scripts/mw_set_clock: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_set_clock 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_set_clock 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Set Microwave Clock 11 | # Description: Set Microwave Clock from system time 12 | ### END INIT INFO 13 | 14 | # Just run the set clock program after a delay 15 | sleep 20 16 | /usr/bin/mw_set_clock 17 | 18 | exit 0 -------------------------------------------------------------------------------- /init_scripts/mw_startup_sound: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_startup_sound 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_startup_sound 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Play Microwave Startup Sound 11 | # Description: Play Microwave Startup Sound on boot 12 | ### END INIT INFO 13 | 14 | /usr/bin/mpg123 /home/ndbroadbent/raspberry_picrowave/audio/startup.mp3 15 | 16 | exit 0 -------------------------------------------------------------------------------- /init_scripts/mw_voice_control: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_voice_control 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_voice_control 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Microwave Voice Control 11 | # Description: Manage Microwave Voice Control process 12 | ### END INIT INFO 13 | 14 | # Find PID of current process 15 | PID=$(ps aux | grep '[p]ocketsphinx_continuous' | awk '{print $2}' | head -n1) 16 | 17 | case "$1" in 18 | start) 19 | if [ -n "$PID" ]; then 20 | echo "mw_voice_control is already running." 21 | else 22 | echo "Starting mw_voice_control..." 23 | mw_voice_control & 24 | fi 25 | ;; 26 | stop) 27 | if [ -n "$PID" ]; then 28 | echo "Stopping mw_voice_control..." 29 | # Doesn't seem to respond to SIGTERM 30 | kill -9 $PID 31 | else 32 | echo "pocketsphinx_continuous is not running." 33 | fi 34 | ;; 35 | restart) 36 | $0 stop 37 | $0 start 38 | ;; 39 | *) 40 | echo "Usage: /etc/init.d/mw_voice_control {start|stop|restart}" 41 | exit 1 42 | ;; 43 | esac 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /init_scripts/mw_web_app: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # /etc/init.d/mw_web_app 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: mw_web_app 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Microwave Web App 11 | # Description: Manage Microwave Web App process 12 | ### END INIT INFO 13 | 14 | # Find PID of current process 15 | PID=$(ps aux | grep '[r]uby \./microwave_webapp\.rb' | awk '{print $2}' | head -n1) 16 | 17 | case "$1" in 18 | start) 19 | if [ -n "$PID" ]; then 20 | echo "mw_web_app is already running." 21 | else 22 | echo "Starting mw_web_app..." 23 | mw_web_app & 24 | fi 25 | ;; 26 | stop) 27 | if [ -n "$PID" ]; then 28 | echo "Stopping mw_web_app..." 29 | kill $PID 30 | else 31 | echo "mw_web_app is not running." 32 | fi 33 | ;; 34 | restart) 35 | $0 stop 36 | $0 start 37 | ;; 38 | *) 39 | echo "Usage: /etc/init.d/mw_web_app {start|stop|restart}" 40 | exit 1 41 | ;; 42 | esac 43 | 44 | exit 0 45 | -------------------------------------------------------------------------------- /init_scripts/piglow_cpu: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # /etc/init.d/piglow_cpu 3 | 4 | ### BEGIN INIT INFO 5 | # Provides: piglow_cpu 6 | # Required-Start: $remote_fs $syslog 7 | # Required-Stop: $remote_fs $syslog 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: Simple script to auto start PiGlow CPU 11 | # Description: Simple script to auto start PiGlow CPU 12 | ### END INIT INFO 13 | 14 | 15 | case "$1" in 16 | start) 17 | sudo python /home/ndbroadbent/PiGlow/Examples/cpu.py & 18 | ;; 19 | stop) 20 | killall python 21 | ;; 22 | *) 23 | exit 1 24 | ;; 25 | 26 | esac 27 | exit 0 -------------------------------------------------------------------------------- /microwave_daemon/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | *.o 3 | *.so 4 | *.log -------------------------------------------------------------------------------- /microwave_daemon/bin/mw_daemon: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /home/ndbroadbent/raspberry_picrowave/microwave_daemon 3 | ./daemon.rb -------------------------------------------------------------------------------- /microwave_daemon/daemon.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Allows multiple programs to communicate with the Arduino 4 | # 5 | require 'rubygems' 6 | require 'socket' 7 | require 'thread' 8 | require 'json' 9 | require File.expand_path('../lib/audio_player', __FILE__) 10 | #require File.expand_path('../microwave', __FILE__) 11 | require File.expand_path('../lib/serial_microwave', __FILE__) 12 | 13 | PORT = 3141 14 | 15 | #@microwave = MicrowaveExt.new 16 | @microwave = SerialMicrowave.new 17 | @server = TCPServer.new(PORT) 18 | 19 | Thread.start(@microwave) do |m| 20 | while true 21 | m.touchpad_loop 22 | end 23 | end 24 | 25 | loop do 26 | Thread.start(@server.accept) do |client| 27 | begin 28 | puts "Client connected." 29 | 30 | while data = client.gets 31 | request = JSON.parse(data) 32 | 33 | if request['get_info'] 34 | # Fetch info and send to TCP client 35 | # client.puts(@microwave.get_info.to_json) 36 | client.puts({}) 37 | 38 | elsif command = request['command'] 39 | # Examples: 40 | # {"command":{"time":10}} 41 | # {"command":{"time":5,"power":7}} 42 | # {"command":"start"} 43 | 44 | commands = [] 45 | if command.is_a?(String) 46 | commands << [command, nil] 47 | 48 | elsif command.is_a?(Hash) 49 | start = command.delete("start") 50 | 51 | command.each {|k, v| commands << [k, v] } 52 | 53 | commands << ["start", nil] if start 54 | end 55 | 56 | reset_time = true 57 | if command['power'] && command['time'] 58 | reset_time = false 59 | end 60 | 61 | commands.each do |cmd| 62 | puts "[MW_DAEMON]: Sending command to Microwave: #{cmd.inspect}" 63 | @microwave.send_command(cmd, reset_time) 64 | end 65 | 66 | end 67 | end 68 | 69 | client.close 70 | puts "Connection closed." 71 | 72 | rescue Exception => ex 73 | puts "Error from microwave daemon!" 74 | p $!, *$@ 75 | 76 | # Close TCP connection 77 | client.close 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /microwave_daemon/ext/microwave/extconf.rb: -------------------------------------------------------------------------------- 1 | # Loads mkmf which is used to make makefiles for Ruby extensions 2 | require 'mkmf' 3 | 4 | dir_config('microwave/microwave') 5 | have_library('wiringPi') 6 | create_makefile('microwave/microwave') 7 | -------------------------------------------------------------------------------- /microwave_daemon/ext/microwave/microwave.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define DEBUG false 8 | #define SERIAL_TIMEOUT 1000 9 | #define VOICE_COMMAND_TIMEOUT 10000 // Allow voice commands up to 10 seconds after the door closes 10 | 11 | #define scanDataPin 0 12 | #define scanLatchPin 2 13 | #define scanClockPin 3 14 | 15 | #define inputClockPin 12 16 | #define inputLatchPin 13 17 | #define inputDataPin 14 18 | 19 | #define outputDataPin 11 20 | #define outputLatchPin 10 21 | #define outputClockPin 6 22 | 23 | #define btn0 0 24 | #define btn1 1 25 | #define btn2 2 26 | #define btn3 3 27 | #define btn4 4 28 | #define btn5 5 29 | #define btn6 6 30 | #define btn7 7 31 | #define btn8 8 32 | #define btn9 9 33 | #define btnWeightDefrost 10 34 | #define btnJetDefrost 11 35 | #define btnPreset 12 36 | #define btnTime 13 37 | #define btnExpress 14 38 | #define btnCancel 15 39 | #define btnPower 16 40 | #define btnMemory 17 41 | #define btnClock 18 42 | #define btnStart 19 43 | 44 | #define newBtnHigh10s 1 45 | #define newBtnHigh20s 2 46 | #define newBtnHigh30s 3 47 | #define newBtnHigh1m 4 48 | #define newBtnHigh2m 5 49 | #define newBtnMed10s 6 50 | #define newBtnMed20s 7 51 | #define newBtnMed30s 8 52 | #define newBtnMed1m 9 53 | #define newBtnMed2m 0 54 | #define newBtnStart 10 55 | #define newBtnStop 11 56 | #define newBtn10s 12 57 | #define newBtn10m 13 58 | #define newBtnMed 14 59 | #define newBtnDefrost 15 60 | #define newBtn1s 16 61 | #define newBtn1m 17 62 | #define newBtnHigh 18 63 | #define newBtnLow 19 64 | 65 | #define doorSwitch 0x40 // B01000000 66 | #define buttonMask 0x3F // B00111111 67 | 68 | const int MAX_SECONDS = 5999; // (99 * 60) + 59 69 | const int POWER_HIGH = 10; 70 | const int POWER_MEDIUM = 7; 71 | const int POWER_LOW = 5; 72 | const int POWER_DEFROST = 3; 73 | const int POWER_ZERO = 0; 74 | 75 | // btnMatrix[VCC Pin][GND Pin] 76 | int btnMatrix[6][6] = { 77 | {-1, -1, -1, -1, -1, 19}, 78 | {-1, 0, 1, 2, 3, -1}, 79 | {-1, 4, 5, 6, 7, -1}, 80 | {10, 8, 9, 12, 15, -1}, 81 | {-1, 13, 18, 11, 16, -1}, 82 | {17, -1, 14, -1, -1, -1} 83 | }; 84 | 85 | char btnNames[][15] = { 86 | "0", 87 | "1", 88 | "2", 89 | "3", 90 | "4", 91 | "5", 92 | "6", 93 | "7", 94 | "8", 95 | "9", 96 | "Weight Defrost", 97 | "Jet Defrost", 98 | "Pre-set", 99 | "Time", 100 | "Express", 101 | "Cancel", 102 | "Power", 103 | "Memory", 104 | "Clock", 105 | "Start" 106 | }; 107 | 108 | char btnNamesNew[][22] = { 109 | "Quickstart Medium 2m", 110 | "Quickstart High 10s", 111 | "Quickstart High 20s", 112 | "Quickstart High 30s", 113 | "Quickstart High 1m", 114 | "Quickstart High 2m", 115 | "Quickstart Medium 10s", 116 | "Quickstart Medium 20s", 117 | "Quickstart Medium 30s", 118 | "Quickstart Medium 1m", 119 | "Start", 120 | "Stop", 121 | "Time 10s", 122 | "Time 10m", 123 | "Power Medium", 124 | "Power Defrost", 125 | "Time 1s", 126 | "Time 1m", 127 | "Power High", 128 | "Power Low" 129 | }; 130 | 131 | uint8_t inputByte, buttonByte, scanMask; 132 | int currentButton = -1; 133 | int lastButton = -1; 134 | int lastPushedButton = -1; 135 | bool on = false; 136 | bool paused = false; 137 | bool doorOpen = false; 138 | bool pendingStart = false; 139 | bool buttonLock = false; 140 | 141 | VALUE commandFromVoice = Qfalse; 142 | int currentPower = 0; 143 | int currentTime = 0; 144 | 145 | char serCommand, serParams; 146 | 147 | unsigned long serialTimer, buttonTimer, countdownTimer, doorTimer; 148 | 149 | VALUE rbMicrowaveExt, rbAudioPlayerModule; 150 | 151 | void playSound(const char *methodName) { 152 | rb_funcall(rbAudioPlayerModule, rb_intern(methodName), 0, NULL); 153 | } 154 | 155 | void printBinary(uint8_t var) { 156 | uint8_t mask; 157 | for (mask = 0x80; mask; mask >>= 1) { // B10000000 158 | printf(mask & var ? "1" : "0"); 159 | } 160 | printf("\n"); 161 | } 162 | 163 | // Check voice commands 164 | bool voiceCommandAllowed() { 165 | if (commandFromVoice != Qfalse) { 166 | return millis() - VOICE_COMMAND_TIMEOUT < doorTimer; 167 | } 168 | return true; 169 | } 170 | 171 | // If multiple buttons are pressed, this 172 | // function just returns the first detected button. 173 | // Stop pressing so many buttons at once. 174 | int pressedButton(uint8_t vccByte, uint8_t gndByte) { 175 | int vcc, gnd, button; 176 | 177 | for (vcc = 0; vcc < 6; vcc++) { 178 | if (vccByte & (0x40 >> vcc)) { // B01000000 179 | for (gnd = 0; gnd < 6; gnd++) { 180 | if (gndByte & (0x20 >> gnd)) { // B00100000 181 | button = btnMatrix[vcc][gnd]; 182 | if (button != -1) { 183 | return button; 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | return -1; 191 | } 192 | 193 | // Find button in matrix 194 | bool findButton(int button, int pins[]) { 195 | int vcc, gnd; 196 | for (vcc = 0; vcc < 6; vcc++) { 197 | for (gnd = 0; gnd < 6; gnd++) { 198 | if (btnMatrix[vcc][gnd] == button) { 199 | pins[0] = vcc; 200 | pins[1] = gnd; 201 | return true; 202 | } 203 | } 204 | } 205 | return false; 206 | } 207 | 208 | void pushButton(int button) { 209 | int buttonPins[2]; 210 | uint8_t vccByte, gndByte; 211 | 212 | if (DEBUG) { 213 | printf("Pressing: "); 214 | printf("%s", btnNames[button]); 215 | } 216 | 217 | if (findButton(button, buttonPins)) { 218 | // Ensure buttons aren't being pushed by multiple threads. 219 | while (buttonLock) { delay(25); } 220 | buttonLock = true; 221 | 222 | // VCC 1,2,3,4,5,6 = 7,6,5,4,3,2 (second register) 223 | // GND 1,2,3,4,5,6 = 7,6,5,4,3,2 (first register) 224 | vccByte = 0x40 >> buttonPins[0]; // B01000000 225 | gndByte = 0x40 >> buttonPins[1]; // B01000000 226 | 227 | // Only need a 'off' delay when pushing the same button twice 228 | if (lastPushedButton == button) { delay(25); } 229 | 230 | digitalWrite(outputLatchPin, LOW); 231 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, vccByte); 232 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, gndByte); 233 | digitalWrite(outputLatchPin, HIGH); 234 | 235 | // Start button needs a longer press 236 | delay(button == btnStart ? 64 : 25); 237 | 238 | digitalWrite(outputLatchPin, LOW); 239 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, 0); 240 | shiftOut(outputDataPin, outputClockPin, MSBFIRST, 0); 241 | digitalWrite(outputLatchPin, HIGH); 242 | 243 | buttonLock = false; 244 | lastPushedButton = button; 245 | } 246 | } 247 | 248 | void setTime(int totalSeconds) { 249 | int minutes, seconds; 250 | 251 | if (totalSeconds > MAX_SECONDS) { return; } 252 | 253 | // Setting time will stop the microwave 254 | if (on) { on = false; } 255 | 256 | currentTime = totalSeconds; 257 | 258 | pushButton(btnTime); 259 | 260 | minutes = totalSeconds / 60; 261 | seconds = totalSeconds % 60; 262 | 263 | if (minutes != 0) { 264 | if (minutes / 10 != 0) { 265 | pushButton(minutes / 10); 266 | } 267 | pushButton(minutes % 10); 268 | } 269 | if (seconds >= 10 || minutes != 0) { 270 | pushButton(seconds / 10); 271 | } 272 | pushButton(seconds % 10); 273 | } 274 | 275 | void incrementTime(int extraSeconds) { 276 | if (currentTime + extraSeconds > MAX_SECONDS) { currentTime = 0; } 277 | setTime(currentTime + extraSeconds); 278 | } 279 | 280 | void setPower(int power) { 281 | if (power < 0 || power > 10) { return; } 282 | 283 | currentPower = power; 284 | 285 | pushButton(btnPower); 286 | 287 | if (power == 10) { 288 | pushButton(1); 289 | pushButton(0); 290 | } else { 291 | pushButton(power); 292 | } 293 | } 294 | 295 | // time is an int, of the form 'hhmm', without leading zeros. 296 | // Times must be between 1:00 and 12:59 297 | void setClock(unsigned int time) { 298 | if (time > 1259 || time < 100 || time % 100 > 59 ) { return; } 299 | 300 | pushButton(btnClock); 301 | pushButton(btnCancel); 302 | 303 | if (time >= 1000) { pushButton(1); } 304 | pushButton(time % 1000 / 100); 305 | pushButton(time % 100 / 10); 306 | pushButton(time % 10); 307 | 308 | pushButton(btnClock); 309 | } 310 | 311 | void start() { 312 | // Only start if door is closed 313 | if (!doorOpen) { 314 | if (currentTime > 0) { 315 | pushButton(btnStart); 316 | on = true; 317 | pendingStart = false; 318 | // Set door timer to 0, so that no further commands are run 319 | doorTimer = 0; 320 | // Set countdown timer to decrement time remaining 321 | countdownTimer = millis(); 322 | // Only play start sound if microwave wasn't previously paused 323 | if (!paused) { playSound("start"); } 324 | paused = false; 325 | } 326 | } else { 327 | // Record the fact that start was pushed while the door was open. 328 | // Once door is closed, start the microwave. 329 | pendingStart = true; 330 | } 331 | } 332 | 333 | void stop() { 334 | pushButton(btnCancel); 335 | on = false; 336 | paused = false; 337 | pendingStart = false; 338 | currentTime = 0; 339 | currentPower = 0; 340 | } 341 | 342 | void pauseMicrowave() { 343 | // Can only pause when microwave is on 344 | if (on) { 345 | pushButton(btnTime); 346 | on = false; 347 | paused = true; 348 | } 349 | } 350 | 351 | void quickStart(int seconds, int power) { 352 | setTime(seconds); 353 | setPower(power); 354 | start(); 355 | } 356 | 357 | uint8_t shiftIn2(int dataPin, int clockPin, int latchPin) { 358 | int i; 359 | int tmp = 0; 360 | uint8_t dataIn = 0; 361 | 362 | digitalWrite(latchPin, HIGH); 363 | delayMicroseconds(300); 364 | digitalWrite(latchPin, LOW); 365 | 366 | for (i = 7; i >= 0; i--) { 367 | digitalWrite(clockPin, LOW); 368 | delayMicroseconds(300); 369 | tmp = digitalRead(dataPin); 370 | if (tmp) { 371 | dataIn = dataIn | (1 << i); 372 | } 373 | digitalWrite(clockPin, HIGH); 374 | delayMicroseconds(100); 375 | } 376 | 377 | return dataIn; 378 | } 379 | 380 | void pushNewButton(int button) { 381 | // Only play button sound for time and power buttons 382 | switch (button) { 383 | case newBtn10s: 384 | case newBtn10m: 385 | case newBtn1s: 386 | case newBtn1m: 387 | case newBtnHigh: 388 | case newBtnMed: 389 | case newBtnLow: 390 | case newBtnDefrost: 391 | playSound("button"); 392 | break; 393 | } 394 | 395 | switch (button) { 396 | case newBtnHigh10s: quickStart(10, POWER_HIGH); break; 397 | case newBtnHigh20s: quickStart(20, POWER_HIGH); break; 398 | case newBtnHigh30s: quickStart(30, POWER_HIGH); break; 399 | case newBtnHigh1m: quickStart(60, POWER_HIGH); break; 400 | case newBtnHigh2m: quickStart(120, POWER_HIGH); break; 401 | case newBtnMed10s: quickStart(10, POWER_MEDIUM); break; 402 | case newBtnMed20s: quickStart(20, POWER_MEDIUM); break; 403 | case newBtnMed30s: quickStart(30, POWER_MEDIUM); break; 404 | case newBtnMed1m: quickStart(60, POWER_MEDIUM); break; 405 | case newBtnMed2m: quickStart(120, POWER_MEDIUM); break; 406 | case newBtn10s: incrementTime(10); break; 407 | case newBtn10m: incrementTime(600); break; 408 | case newBtn1s: incrementTime(1); break; 409 | case newBtn1m: incrementTime(60); break; 410 | // Can't set power while microwave is on 411 | case newBtnHigh: if (!on) { setPower(POWER_HIGH); }; break; 412 | case newBtnMed: if (!on) { setPower(POWER_MEDIUM); }; break; 413 | case newBtnLow: if (!on) { setPower(POWER_LOW); }; break; 414 | case newBtnDefrost: if (!on) { setPower(POWER_DEFROST); }; break; 415 | case newBtnStart: start(); break; 416 | case newBtnStop: stop(); playSound("stop"); break; 417 | } 418 | } 419 | 420 | // args: command, param, command_from_voice? 421 | static VALUE method_send_command(VALUE self, VALUE args) { 422 | VALUE command, param; 423 | char * cmdStr; 424 | char * paramStr = ""; 425 | int i; 426 | 427 | long len = RARRAY_LEN(args); 428 | 429 | if (len > 3 || len == 0) { 430 | rb_raise(rb_eArgError, "wrong number of arguments"); 431 | } 432 | 433 | command = rb_ary_entry(args, 0); 434 | cmdStr = StringValueCStr(command); 435 | 436 | if (len >= 2) { 437 | param = rb_ary_entry(args, 1); 438 | } 439 | 440 | // Voice - indicates that the command came from latent speech recognition (no keyword prefix). 441 | // The command will only be executed if the microwave door was recently closed. 442 | if (len >= 3) { 443 | commandFromVoice = rb_ary_entry(args, 2); 444 | } else { 445 | commandFromVoice = Qfalse; 446 | } 447 | 448 | if (strcmp(cmdStr, "button") == 0) { 449 | if (TYPE(param) == T_STRING) { 450 | paramStr = StringValueCStr(param); 451 | 452 | for (i = 0; i < sizeof(btnNames) / sizeof(btnNames[0]); i++) { 453 | if (strcmp(btnNames[i], paramStr) == 0) { 454 | pushButton(i); 455 | break; 456 | } 457 | } 458 | } 459 | } 460 | 461 | if (strcmp(cmdStr, "new_button") == 0) { 462 | if (TYPE(param) == T_STRING) { 463 | paramStr = StringValueCStr(param); 464 | 465 | for (i = 0; i < sizeof(btnNamesNew) / sizeof(btnNamesNew[0]); i++) { 466 | if (strcmp(btnNamesNew[i], paramStr) == 0) { 467 | pushNewButton(i); 468 | break; 469 | } 470 | } 471 | } 472 | } 473 | 474 | else if (strcmp(cmdStr, "clock") == 0) { 475 | // clock | 1135 => Set clock to 11:35 476 | setClock(NUM2INT(param)); 477 | } 478 | 479 | else if (strcmp(cmdStr, "time") == 0) { 480 | if (voiceCommandAllowed()) { 481 | setTime(NUM2INT(param)); 482 | } 483 | } 484 | 485 | else if (strcmp(cmdStr, "power") == 0) { 486 | // power (integer or level name) 487 | // Integer: 6 => Set power to 6 488 | // Level name: high => Set power to high (10) 489 | if (voiceCommandAllowed()) { 490 | if (TYPE(param) == T_STRING) { 491 | paramStr = StringValueCStr(param); 492 | if (strcmp(paramStr, "high") == 0) { setPower(POWER_HIGH); } 493 | else if (strcmp(paramStr, "medium") == 0) { setPower(POWER_MEDIUM); } 494 | else if (strcmp(paramStr, "low") == 0) { setPower(POWER_LOW); } 495 | else if (strcmp(paramStr, "defrost") == 0) { setPower(POWER_DEFROST); } 496 | else if (strcmp(paramStr, "off") == 0) { setPower(POWER_ZERO); } 497 | 498 | } else { 499 | setPower(NUM2INT(param)); 500 | } 501 | } 502 | } 503 | 504 | else if (strcmp(cmdStr, "start") == 0) { 505 | if (voiceCommandAllowed()) { start(); } 506 | } 507 | 508 | else if (strcmp(cmdStr, "stop") == 0) { 509 | if (voiceCommandAllowed()) { 510 | stop(); 511 | playSound("stop"); 512 | } 513 | } 514 | 515 | else if (strcmp(cmdStr, "pause") == 0) { 516 | pauseMicrowave(); 517 | } 518 | 519 | return self; 520 | } 521 | 522 | 523 | VALUE method_get_info(VALUE self) { 524 | VALUE info = rb_hash_new(); 525 | 526 | rb_hash_aset(info, ID2SYM(rb_intern("on")), on ? Qtrue : Qfalse); 527 | rb_hash_aset(info, ID2SYM(rb_intern("paused")), paused ? Qtrue : Qfalse); 528 | rb_hash_aset(info, ID2SYM(rb_intern("door_open")), doorOpen ? Qtrue : Qfalse); 529 | rb_hash_aset(info, ID2SYM(rb_intern("power")), INT2FIX(currentPower)); 530 | rb_hash_aset(info, ID2SYM(rb_intern("time")), INT2FIX(currentTime)); 531 | 532 | return info; 533 | } 534 | 535 | 536 | // Can't do an infinite loop in C, or the Ruby interpreter gets locked up 537 | VALUE method_touchpad_loop(VALUE self) { 538 | int validReading = 0; 539 | 540 | inputByte = 0; 541 | // B00000010 B01000000 542 | for (scanMask = 0x2; scanMask <= 0x40; scanMask <<= 1) { 543 | digitalWrite(scanLatchPin, LOW); 544 | shiftOut(scanDataPin, scanClockPin, MSBFIRST, scanMask); 545 | digitalWrite(scanLatchPin, HIGH); 546 | 547 | // printf("scanMask:\n"); 548 | // printBinary(scanMask); 549 | 550 | validReading = 0; 551 | while (validReading == 0) { 552 | // Ignore first bit (floating) 553 | inputByte = shiftIn2(inputDataPin, inputClockPin, inputLatchPin) & 0x7F; // B01111111 554 | delay(2); 555 | if (inputByte == (shiftIn2(inputDataPin, inputClockPin, inputLatchPin) & 0x7F)) 556 | validReading = 1; 557 | } 558 | 559 | // printf("inputByte:\n"); 560 | // printBinary(inputByte); 561 | 562 | // 1 = closed, 0 = open 563 | if (!(inputByte & doorSwitch)) { 564 | // Stop if door is opened 565 | if (on) { stop(); } 566 | if (!doorOpen) { 567 | if (DEBUG) { printf("Door is now open.\n"); } 568 | doorOpen = true; 569 | } 570 | } else { 571 | // Set door state if door is now closed 572 | if (doorOpen) { 573 | if (DEBUG) { printf("Door is now closed.\n"); } 574 | doorOpen = false; 575 | doorTimer = millis(); // Allow voice commands up to 7 seconds after door was closed 576 | 577 | // Check to see if start was pressed while door was open 578 | // If so, start the microwave now. 579 | if (pendingStart) { start(); } 580 | } 581 | } 582 | 583 | buttonByte = inputByte & buttonMask; 584 | 585 | if (buttonByte != 0) { 586 | currentButton = pressedButton(scanMask, buttonByte); 587 | 588 | if (currentButton != -1 && currentButton != lastButton) { 589 | pushNewButton(currentButton); 590 | 591 | lastButton = currentButton; 592 | } 593 | 594 | buttonTimer = millis(); 595 | } else { 596 | // Debounce buttons with 100ms delay 597 | if (lastButton != -1 && millis() - 100 > buttonTimer) { 598 | lastButton = -1; 599 | buttonTimer = 0; 600 | } 601 | } 602 | } 603 | 604 | if (on) { 605 | // Decrement time counter every 1000ms 606 | if (millis() - 1000 > countdownTimer) { 607 | countdownTimer = millis(); 608 | currentTime -= 1; 609 | if (currentTime <= 0) { 610 | stop(); 611 | playSound("finished"); 612 | } 613 | } 614 | } 615 | 616 | delay(10); 617 | 618 | return self; 619 | } 620 | 621 | // The initialization method for this module 622 | void Init_microwave() { 623 | if (wiringPiSetup() == -1) 624 | printf("wiringPi setup failed!\n"); 625 | 626 | // Set pin modes 627 | pinMode(scanDataPin, OUTPUT); 628 | pinMode(scanLatchPin, OUTPUT); 629 | pinMode(scanClockPin, OUTPUT); 630 | 631 | pinMode(inputDataPin, INPUT); 632 | pinMode(inputLatchPin, OUTPUT); 633 | pinMode(inputClockPin, OUTPUT); 634 | 635 | pinMode(outputDataPin, OUTPUT); 636 | pinMode(outputLatchPin, OUTPUT); 637 | pinMode(outputClockPin, OUTPUT); 638 | 639 | // Setup class 640 | rbMicrowaveExt = rb_define_class("MicrowaveExt", rb_cObject); 641 | rb_define_method(rbMicrowaveExt, "touchpad_loop", method_touchpad_loop, 0); 642 | rb_define_method(rbMicrowaveExt, "send_command", method_send_command, -2); 643 | rb_define_method(rbMicrowaveExt, "get_info", method_get_info, 0); 644 | 645 | rbAudioPlayerModule = rb_const_get(rb_cObject, rb_intern("AudioPlayer")); 646 | } 647 | -------------------------------------------------------------------------------- /microwave_daemon/lib/audio_player.rb: -------------------------------------------------------------------------------- 1 | class AudioPlayer 2 | class << self 3 | def button; play "button"; end 4 | def start; play "start"; end 5 | def stop; play "stop"; end 6 | def finished; play "finished"; end 7 | 8 | private 9 | 10 | def play(file) 11 | path = File.expand_path("../../../audio/#{file}.mp3", __FILE__) 12 | if File.exist?(path) 13 | `mpg123 "#{path}" > /dev/null & 2>&1` 14 | else 15 | puts "Error! Sound does not exist: #{path}" 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /microwave_daemon/lib/client.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | require 'json' 3 | PORT = 3141 4 | 5 | class Microwave 6 | class Daemon 7 | class Client 8 | attr_accessor :info 9 | 10 | def initialize 11 | @socket = TCPSocket.new('localhost', PORT) 12 | @info = {} 13 | end 14 | 15 | def fetch_info 16 | send_request(:get_info => true) 17 | @info = JSON.parse(@socket.gets) 18 | # Symbolize keys 19 | @info = @info.inject({}) do |hash, (k,v)| 20 | hash[k.to_sym] = v 21 | hash 22 | end 23 | end 24 | 25 | def send_request(command) 26 | begin 27 | @socket.puts(command.to_json) 28 | 29 | rescue Exception => ex 30 | puts "Error from Daemon TCP connection!" 31 | puts ex.inspect 32 | # Reset socket 33 | connected = false 34 | while !connected 35 | begin 36 | @socket = TCPSocket.new('localhost', PORT) 37 | connected = true 38 | rescue 39 | sleep 1 40 | end 41 | end 42 | end 43 | end 44 | 45 | def start(time, power_level) 46 | command = {:time => time, :power => power_level, :start => true} 47 | puts "JSON command: #{command.inspect}" 48 | send_request(:command => command) 49 | end 50 | 51 | # Pause by pushing the 'time' button 52 | def pause 53 | puts "Pausing Microwave..." 54 | send_request(:command => :pause) 55 | end 56 | 57 | def stop 58 | puts "Stopping Microwave..." 59 | send_request(:command => :stop) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /microwave_daemon/lib/serial_microwave.rb: -------------------------------------------------------------------------------- 1 | require 'serialport' 2 | 3 | class SerialMicrowave 4 | PORT_STR = "/dev/ttyACM0" 5 | 6 | CMD_CLOCK = 0 7 | CMD_MODE = 1 8 | CMD_TIME = 5 9 | CMD_POWER = 4 10 | 11 | POWER_LEVELS = { 12 | 'high' => 10, 13 | 'medium' => 7, 14 | 'low' => 5, 15 | 'defrost' => 3 16 | } 17 | 18 | WEB_BUTTON_MAPPINGS = { 19 | "Quickstart Medium 2m" => [['time', 120], ['power', 'medium']], 20 | "Quickstart High 10s" => [['time', 10], ['power', 'high']], 21 | "Quickstart High 20s" => [['time', 20], ['power', 'high']], 22 | "Quickstart High 30s" => [['time', 30], ['power', 'high']], 23 | "Quickstart High 1m" => [['time', 60], ['power', 'high']], 24 | "Quickstart High 2m" => [['time', 120], ['power', 'high']], 25 | "Quickstart Medium 10s" => [['time', 10], ['power', 'medium']], 26 | "Quickstart Medium 20s" => [['time', 20], ['power', 'medium']], 27 | "Quickstart Medium 30s" => [['time', 30], ['power', 'medium']], 28 | "Quickstart Medium 1m" => [['time', 60], ['power', 'medium']], 29 | "Start" => [['button', 10]], 30 | "Stop" => [['button', 10]], 31 | "Time 10s" => [['time', 120]], 32 | "Time 10m" => [['time', 120]], 33 | "Power Medium" => [['time', 120]], 34 | "Power Defrost" => [['time', 120]], 35 | "Time 1s" => [['time', 120]], 36 | "Time 1m" => [['time', 120]], 37 | "Power High" => [['time', 120]], 38 | "Power Low" => [['time', 120]] 39 | } 40 | 41 | def initialize 42 | unless ENV['DEBUGGING'] 43 | @serial = SerialPort.new(PORT_STR, 9600, 8, 1, SerialPort::NONE) 44 | end 45 | end 46 | 47 | 48 | def send_command(command, reset_time = true) 49 | case command[0] 50 | when 'clock' 51 | hour, min = command[1].to_s.rjust(4, '0').scan(/\d\d/).map(&:to_i) 52 | send_command_packet(0, hour, min) 53 | 54 | when 'time' 55 | send_clear 56 | set_microwave 57 | send_time command[1].to_i 58 | @time = command[1].to_i 59 | 60 | 61 | when 'power' 62 | if reset_time 63 | send_command(['time', @time || 15]) 64 | sleep 0.2 65 | end 66 | 67 | level = POWER_LEVELS[command[1].to_s] || 10 68 | send_power level 69 | 70 | sleep 0.2 71 | 72 | when 'button' 73 | send_button command[1] 74 | 75 | when 'new_button' 76 | puts 'new button!' 77 | puts command[1].inspect 78 | 79 | if web_commands = WEB_BUTTON_MAPPINGS[command[1]] 80 | web_commands.each do |cmd| 81 | send_command cmd, false 82 | end 83 | end 84 | end 85 | end 86 | 87 | 88 | private 89 | 90 | def send_command_packet(cmd, param1, param2 = 0) 91 | puts "sending cmd: [#{cmd}, #{param1}, #{param2}]" 92 | 93 | unless ENV['DEBUGGING'] 94 | @serial.write [0x01, cmd, param1, param2, 0x17].pack('c*') 95 | end 96 | 97 | sleep 0.3 98 | end 99 | 100 | def send_clear 101 | send_command_packet 5, 11 102 | end 103 | 104 | def send_time(time) 105 | send_command_packet 2, 0, time 106 | end 107 | 108 | def send_power(power) 109 | send_command_packet 4, power 110 | end 111 | 112 | def set_microwave 113 | send_command_packet 1, 1 114 | end 115 | 116 | def send_button(button) 117 | send_command_packet 5, button 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /microwave_daemon/mwtest.rb: -------------------------------------------------------------------------------- 1 | require './microwave' 2 | @m = MicrowaveExt.new 3 | -------------------------------------------------------------------------------- /scripts/setup_audio: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | amixer cset numid=3 1 3 | amixer set PCM -- 95% 4 | -------------------------------------------------------------------------------- /setup_bin_and_init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Creates /usr/bin/ and /etc/init.d/ symlinks, and runs update-rc.d 3 | for f in $PWD/*/bin/*; do 4 | ln -fs "$f" "/usr/bin/$(basename $f)" 5 | done 6 | 7 | for f in $PWD/init_scripts/*; do 8 | name="$(basename $f)" 9 | ln -fs "$f" "/etc/init.d/$name" 10 | sudo update-rc.d "$name" defaults 11 | done 12 | -------------------------------------------------------------------------------- /sinatra_app/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_JOBS: 8 3 | -------------------------------------------------------------------------------- /sinatra_app/.gitignore: -------------------------------------------------------------------------------- 1 | recent_barcodes.yml 2 | -------------------------------------------------------------------------------- /sinatra_app/Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'sinatra' 4 | gem 'thin' -------------------------------------------------------------------------------- /sinatra_app/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | daemons (1.1.9) 5 | eventmachine (1.0.3) 6 | rack (1.5.2) 7 | rack-protection (1.5.0) 8 | rack 9 | sinatra (1.4.3) 10 | rack (~> 1.4) 11 | rack-protection (~> 1.4) 12 | tilt (~> 1.3, >= 1.3.4) 13 | thin (1.5.1) 14 | daemons (>= 1.0.9) 15 | eventmachine (>= 0.12.6) 16 | rack (>= 1.0.0) 17 | tilt (1.4.1) 18 | 19 | PLATFORMS 20 | ruby 21 | 22 | DEPENDENCIES 23 | sinatra 24 | thin 25 | -------------------------------------------------------------------------------- /sinatra_app/bin/mw_web_app: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /home/ndbroadbent/raspberry_picrowave/sinatra_app 3 | ./microwave_webapp.rb -------------------------------------------------------------------------------- /sinatra_app/microwave_webapp.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'sinatra' 3 | require 'yaml' 4 | require 'json' 5 | require File.expand_path('../../microwave_daemon/lib/client', __FILE__) 6 | 7 | configure do 8 | set server: 'thin', bind: '0.0.0.0', port: ENV['PORT'] || '80', connections: [] 9 | 10 | connected = false 11 | while !connected 12 | begin 13 | set :microwave, Microwave::Daemon::Client.new 14 | connected = true 15 | rescue 16 | puts "Could not connect to microwave daemon... retrying after 1s" 17 | sleep 1 18 | end 19 | end 20 | 21 | set :barcodes_file, File.expand_path("../recent_barcodes.yml", __FILE__) 22 | end 23 | 24 | def fetch_info 25 | begin 26 | settings.microwave.fetch_info 27 | info = settings.microwave.info 28 | 29 | seconds = info[:time].to_i % 60 30 | minutes = info[:time].to_i / 60 31 | info[:formatted_time] = format("%d:%02d", minutes, seconds) 32 | 33 | info[:power_string] = case info[:power] 34 | when 0 35 | "Off" 36 | when 3 37 | "Defrost" 38 | when 5 39 | "Low" 40 | when 7 41 | "Medium" 42 | when 10 43 | "High" 44 | else 45 | info[:power] 46 | end 47 | rescue 48 | info = nil 49 | end 50 | 51 | return info 52 | end 53 | 54 | get '/' do 55 | if File.exists?(settings.barcodes_file) 56 | @barcodes = YAML.load_file(settings.barcodes_file) 57 | else 58 | @barcodes = {known: [], unknown: []} 59 | end 60 | 61 | @info = fetch_info 62 | 63 | erb :touchpad 64 | end 65 | 66 | post '/clear_barcodes' do 67 | if File.exists?(settings.barcodes_file) 68 | File.delete(settings.barcodes_file) 69 | end 70 | redirect to('/') 71 | end 72 | 73 | get '/events', provides: 'text/event-stream' do 74 | response.headers['X-Accel-Buffering'] = 'no' # Disable buffering for nginx 75 | stream :keep_open do |out| 76 | settings.connections << out 77 | out.callback { settings.connections.delete(out) } 78 | end 79 | end 80 | 81 | def format_event(body) 82 | "data: #{body}\n\n" 83 | end 84 | 85 | info_thread = Thread.new do 86 | previous_data = nil 87 | while true 88 | if settings.connections.any? 89 | if File.exists?(settings.barcodes_file) 90 | barcodes = YAML.load_file(settings.barcodes_file) 91 | else 92 | barcodes = {known: [], unknown: []} 93 | end 94 | info = fetch_info || {error: true} 95 | 96 | data = {info: info, barcodes: barcodes} 97 | 98 | if data != previous_data 99 | event = format_event(data.to_json) 100 | settings.connections.each { |out| out << event } 101 | 102 | previous_data = data 103 | end 104 | end 105 | 106 | sleep 0.2 107 | end 108 | end 109 | 110 | get '/button/:name' do 111 | puts "Pressing button: #{params[:name]}" 112 | settings.microwave.send_request(:command => {new_button: params[:name]}) 113 | end 114 | -------------------------------------------------------------------------------- /sinatra_app/public/Raspberry_Pi_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndbroadbent/raspberry_picrowave/5b762bb7905d54544557618ce6c6ed6c34e72605/sinatra_app/public/Raspberry_Pi_Logo.png -------------------------------------------------------------------------------- /sinatra_app/public/application.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #779; 3 | font-family: 'Merriweather Sans', serif; 4 | color: white; 5 | } 6 | 7 | .btn { 8 | font-family: 'Merriweather Sans', serif; 9 | box-shadow: 0 1px 3px #111 !important; 10 | font-weight: bold; 11 | } 12 | 13 | .container { 14 | margin-top: 40px; 15 | width: 270px; 16 | padding: 12px; 17 | text-align: center; 18 | background-image: url("touchpad-background.jpg"); 19 | } 20 | 21 | .barcodes { 22 | background-color: #668; 23 | border: 3px solid #558; 24 | width: 264px; 25 | padding: 12px; 26 | margin: auto; 27 | } 28 | 29 | .barcodes h3 { 30 | text-align: center; 31 | } 32 | 33 | .barcodes li { 34 | margin-left: 12px; 35 | } 36 | 37 | .barcodes li.divider { 38 | list-style: none; 39 | border-bottom: 1px solid #557; 40 | margin: 15px 0; 41 | } 42 | 43 | .barcodes .btn { 44 | margin-left: 7px; 45 | } 46 | 47 | .barcodes hr { 48 | border-color: #557; 49 | } 50 | 51 | .barcodes form { 52 | margin-top: 20px; 53 | text-align: center; 54 | } 55 | 56 | .screen { 57 | width: 221px; 58 | height: 90px; 59 | background-color: #222; 60 | border: 4px solid #555; 61 | margin: auto; 62 | margin-bottom: 55px; 63 | margin-top: 25px; 64 | } 65 | 66 | .screen table { 67 | font-size: 14px; 68 | text-align: center; 69 | margin: auto; 70 | margin-top: 4px; 71 | } 72 | .screen table th { font-weight: normal; text-align: right; } 73 | .screen table td { font-weight: bold; text-align: left; padding-left: 8px; } 74 | 75 | .buttons { 76 | position: relative; 77 | } 78 | 79 | .btn-row { margin-bottom: 26px; clear: both; } 80 | 81 | .main .btn { 82 | width: 95px; 83 | margin-bottom: 22px; 84 | font-size: 15px; 85 | } 86 | .main .btn-row { padding: 0 28px;} 87 | 88 | .main .btn:first-child { float: left; } 89 | .main .btn:last-child { float: right; } 90 | 91 | .raspberry-pi-logo { 92 | width: 75px; 93 | position: absolute; 94 | top: 120px; 95 | left: 34px; 96 | } 97 | 98 | .controls .btn { 99 | float: right; 100 | } 101 | .power .btn { font-size: 14px; } 102 | 103 | /* .power .btn-row, .time .btn-row { 104 | padding: 0 20px; 105 | }*/ 106 | 107 | .autostart-info { 108 | margin: 0; 109 | } 110 | -------------------------------------------------------------------------------- /sinatra_app/public/application.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | $(document).on('click', 'button', function(event) { 3 | var buttonName = $(event.currentTarget).data('name'); 4 | $.get('/button/' + buttonName); 5 | }); 6 | 7 | source = new EventSource('/events'); 8 | source.addEventListener('open', function(e) { 9 | console.log("Connection opened"); 10 | }); 11 | 12 | source.addEventListener('error', function(e) { 13 | console.log("Connection error"); 14 | if (e.readyState == EventSource.CLOSED) { 15 | console.log("Connection closed"); 16 | } 17 | }); 18 | 19 | source.addEventListener('message', function(e) { 20 | data = JSON.parse(e.data) 21 | var info = data.info, barcodes = data.barcodes; 22 | 23 | if (info.error) { 24 | $('.screen .info').hide(); 25 | $('.screen .error').show(); 26 | } else { 27 | $('.screen.error').hide(); 28 | $('.screen .on').html(info.on ? 'On' : 'Off'); 29 | $('.screen .door').html(info.door_open ? 'Open' : 'Closed'); 30 | $('.screen .time').html(info.formatted_time); 31 | $('.screen .power').html(info.power_string); 32 | } 33 | 34 | if (barcodes.unknown.length || barcodes.known.length){ 35 | var barcodeHTML = ""; 36 | $.each(barcodes.unknown, function(i, val){ 37 | barcodeHTML += "
  • " + val + 38 | ': Add Product' 39 | "
  • "; 40 | }); 41 | 42 | if (barcodes.unknown.length && barcodes.known.length) { barcodeHTML += '
  • ' } 43 | 44 | $.each(barcodes.known, function(i, val){ 45 | barcodeHTML += "
  • " + val + 46 | ': Edit Product' 47 | "
  • "; 48 | }); 49 | 50 | $('.barcodes ul.barcodes-list').html(barcodeHTML); 51 | $('.barcodes').show(); 52 | } else { 53 | $('.barcodes').hide(); 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /sinatra_app/public/jquery-1.10.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.10.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license 2 | //@ sourceMappingURL=jquery-1.10.1.min.map 3 | */ 4 | (function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.1",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.parentWindow;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.frameElement&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),pt("value",ht,r.attributes&&r.input),r.getElementsByTagName=ct(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ct(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null) 5 | }),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); 6 | u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("