[
  {
    "path": ".gitignore",
    "content": "lib-cov\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.gz\n\npids\nlogs\nresults\nnode_modules\n.idea\nnpm-debug.log\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "nodecast\n========\n\nChromeCast Emulation with Node.js\n\n* Make sure to install all dependencies using `npm install`\n* Start the app (usage: `node server.js --name Custom_Chromecast_Name`)\n* Open up a browser (tested with Chrome 29 and Firefox) and point it to http://localhost:8008\n* nodecast should show up in Chrome Cast enabled applications (Google Music, YouTube)\n* You should allow popups for http://localhost:8008 to see the video/music screen\n\nTested with the following services: YouTube (Desktop, Android), Google Music (Android)\n\n#### Notes\nInspired by [leapcast](https://github.com/dz0ny/leapcast) by [dz0ny](ttps://github.com/dz0ny)\n\n\n[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/mauimauer/nodecast/trend.png)](https://bitdeli.com/free \"Bitdeli Badge\")\n\n"
  },
  {
    "path": "app.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\r\n    <service xmlns='urn:dial-multiscreen-org:schemas:dial'>\r\n        <name>#name#</name>\r\n        <options allowStop='true'/>\r\n        <activity-status xmlns='urn:chrome.google.com:cast'>\r\n            <description>Legacy</description>\r\n        </activity-status>\r\n        <servicedata xmlns='urn:chrome.google.com:cast'>\r\n            <connectionSvcURL>#connectionSvcURL#</connectionSvcURL>\r\n            <protocols>#protocols#</protocols>\r\n        </servicedata>\r\n        <state>#state#</state>\r\n        #link#\r\n    </service>"
  },
  {
    "path": "apps/app.js",
    "content": "var fs = require('fs');\r\nvar querystring = require('querystring');\r\n\r\nfunction App(addr, name, url, protocols) {\r\n\tthis.addr = addr;\r\n    this.config = {\r\n    \tname: name,\r\n    \tstate: \"stopped\",\r\n    \tlink: \"\",\r\n    \tconnectionSvcURL: \"\",\r\n    \tprotocols: [\"ramp\"],\r\n    \tapp_url: \"\",\r\n    \turl: url\r\n    }\r\n\r\n\tif(Array.isArray(protocols) ) {\r\n\t\tfor(var i = 0; i < protocols.length; i++) {\r\n\t\t\tthis.config.protocols.push(protocols[i]);\r\n\t\t}\r\n\t}\r\n\r\n    this.receivers = [];\r\n    this.remotes = [];\r\n    this.messageQueue = [];\r\n}\r\n\r\nApp.prototype.getName = function() {\r\n\treturn this.config.name;\r\n}\r\n\r\nApp.prototype.registerSession = function(connection) {\r\n\tvar me = this;\r\n\tconnection.on('message', function(message) {\r\n\t\t//if(message.utf8Data.indexOf('ping') != -1) {\r\n\t\t//\tconnection.send(message.utf8Data.replace('ping', 'pong'));\r\n\t\t//} else {\r\n\t\t\tif(me.receivers.length == 0) {\r\n\t\t\t\tconsole.log(\"buffering msg for receiver\");\r\n\t\t\t\tme.messageQueue.push(message.utf8Data);\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log(\"relaying msg to receiver\");\r\n\t\t\t\tme.receivers[0].send(message.utf8Data);\r\n\t\t\t}\r\n\r\n\t\tif(message.utf8Data.indexOf('ping') == -1 && message.utf8Data.indexOf('pong') == -1) {\r\n\t\t\tconsole.log(\"-->to receiver: \"+ message.utf8Data);\r\n\t\t}\r\n\t\t//}\r\n\t});\r\n\r\n\tconnection.on('close', function(closeReason, description) {\r\n\t\tvar i = me.remotes.indexOf(connection);\t\r\n\t\tme.remotes.splice(i,1);\r\n\t\tconsole.log(\"Closed SessionChannel\");\r\n\t});\r\n\r\n\tconsole.log(\"Opened SessionChannel\");\r\n\tthis.remotes.push(connection);\r\n}\r\n\r\nApp.prototype.registerReceiver = function(connection) {\r\n\r\n\tvar me = this;\r\n\tconnection.on('message', function(message) {\r\n\t\t//if(message.utf8Data.indexOf('ping') != -1) {\r\n\t\t//\tconnection.send(message.utf8Data.replace('ping', 'pong'));\r\n\t\t//} else {\r\n\t\t\tif(me.remotes.length > 0)\r\n\t\t\t\tme.remotes[0].send(message.utf8Data);\r\n\t\t//}\r\n\r\n\t\tif(message.utf8Data.indexOf('ping') == -1 && message.utf8Data.indexOf('pong') == -1) {\r\n\t\t\tconsole.log(\"-->to remote: \"+ message.utf8Data);\r\n\t\t}\r\n\t});\r\n\r\n\tconnection.on('close', function(closeReason, description) {\r\n\t\tvar i = me.receivers.indexOf(connection);\t\r\n\t\tme.receivers.splice(i,1);\r\n\t\tconsole.log(\"Closed ReceiverChannel\");\r\n\t});\r\n\r\n\tconsole.log(\"Opened ReceiverChannel\");\r\n\tthis.receivers.push(connection);\r\n\r\n\twhile(me.messageQueue.length > 0) {\r\n\t\tconnection.send(me.messageQueue.shift());\r\n\t}\r\n}\r\n\r\nApp.prototype.getProtocols = function() {\r\n\tvar p = \"\";\r\n\r\n\tif(this.config.state == \"running\") {\r\n\t\tfor(var i = 0; i < this.config.protocols.length; i++) {\r\n\t\t\tp += \"<protocol>\"+this.config.protocols[i]+\"</protocol>\";\r\n\t\t}\r\n\t}\r\n\r\n\treturn p;\r\n}\r\n\r\nApp.prototype.registerApi = function(app) {\r\n\tvar me = this;\r\n\tapp.get(\"/apps/\"+me.config.name, function(req, res) {\r\n\t\tfs.readFile('./app.xml', 'utf8', function (err,data) {\r\n\t\t\tdata = data.replace(\"#name#\", me.config.name)\r\n\t\t\t\t.replace(\"#connectionSvcURL#\",me.config.connectionSvcURL)\r\n\t\t\t\t.replace('#protocols#',me.getProtocols())\r\n\t\t\t\t.replace('#state#', me.config.state)\r\n\t\t\t\t.replace('#link#', me.config.link);\r\n\t\t\tres.type('xml');\r\n\t\t\tres.setHeader(\"Access-Control-Allow-Method\", \"GET, POST, DELETE, OPTIONS\");\r\n\t\t\tres.setHeader(\"Access-Control-Expose-Headers\", \"Location\");\r\n\t\t\tres.setHeader(\"Cache-control\", \"no-cache, must-revalidate, no-store\");\r\n\t\t\tres.send(data);\r\n\t\t});\r\n\t});\r\n\r\n\tapp.delete(\"/apps/\"+me.config.name+\"/web-1\", function(req, res) {\r\n\t\tme.config.state = \"stopped\";\r\n\t\tme.config.link = \"\";\r\n\t\tme.config.connectionSvcURL = \"\";\r\n\r\n\t\tfs.readFile('./app.xml', 'utf8', function (err,data) {\r\n\r\n\t\t\tif(global.stageConnection)\r\n\t\t\t\tglobal.stageConnection.send(JSON.stringify({\r\n\t\t\t\t\tcmd: \"close\",\r\n\t\t\t\t\tapp: me.config.name\r\n\t\t\t\t}))\r\n\r\n\t\t\tdata = data.replace(\"#name#\", me.config.name)\r\n\t\t\t\t.replace(\"#connectionSvcURL#\",me.config.connectionSvcURL)\r\n\t\t\t\t.replace('#protocols#',me.getProtocols())\r\n\t\t\t\t.replace('#state#', me.config.state)\r\n\t\t\t\t.replace('#link#', me.config.link);\r\n\t\t\tres.type('xml');\r\n\t\t\tres.setHeader(\"Access-Control-Allow-Method\", \"GET, POST, DELETE, OPTIONS\");\r\n\t\t\tres.setHeader(\"Access-Control-Expose-Headers\", \"Location\");\r\n\t\t\tres.setHeader(\"Cache-control\", \"no-cache, must-revalidate, no-store\");\r\n\t\t\tres.send(data);\r\n\t\t});\r\n\t});\r\n\r\n\tapp.post(\"/apps/\"+me.config.name, function(req, res) {\r\n\t\tme.config.state = \"running\";\r\n\t\tme.config.link = \"<link rel='run' href='web-1'/>\";\r\n\t\tme.config.connectionSvcURL = \"http://\"+me.addr+\":8008/connection/\"+me.config.name\r\n\r\n\t\tif(global.stageConnection)\r\n\t\t\tglobal.stageConnection.send(JSON.stringify({\r\n\t\t\t\tcmd: \"show\",\r\n\t\t\t\tapp: me.config.name,\r\n\t\t\t\turl:  me.config.url.replace('$query',req.rawBody)\r\n\t\t\t}))\r\n\r\n\t\tres.setHeader(\"Location\", \"http://\"+me.addr+\":8008/apps/\"+me.config.name+\"/web-1\");\r\n\t\tres.send(201,\"\");\r\n\t});\r\n}\r\n\r\nmodule.exports = App;"
  },
  {
    "path": "apps/apps.js",
    "content": "var App = require('./app.js');\r\n\r\nfunction Apps() {\r\n    this.registered = {};\r\n};\r\n\r\nApps.prototype.init = function(fs, app) {\r\n    var me = this;\r\n};\r\n\r\nApps.prototype.registerApp = function(express, addr, name, url, protocols) {\r\n    var app = new App(addr, name, url, protocols);\r\n    this.registered[name] = app;\r\n    app.registerApi(express);\r\n    console.log(\"Registered App: \"+ name);\r\n}\r\n\r\nvar apps = new Apps();\r\nmodule.exports = apps;"
  },
  {
    "path": "device-desc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n    <root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">\r\n        <specVersion>\r\n        <major>1</major>\r\n        <minor>0</minor>\r\n        </specVersion>\r\n        <URLBase>#base#</URLBase>\r\n        <device>\r\n            <deviceType>urn:schemas-upnp-org:device:dail:1</deviceType>\r\n            <friendlyName>#name#</friendlyName>\r\n            <manufacturer>Google Inc.</manufacturer>\r\n            <modelName>Eureka Dongle</modelName>\r\n            <UDN>uuid:#uuid#</UDN>\r\n            <serviceList>\r\n                <service>\r\n                    <serviceType>urn:schemas-upnp-org:service:dail:1</serviceType>\r\n                    <serviceId>urn:upnp-org:serviceId:dail</serviceId>\r\n                    <controlURL>/ssdp/notfound</controlURL>\r\n                    <eventSubURL>/ssdp/notfound</eventSubURL>\r\n                    <SCPDURL>/ssdp/notfound</SCPDURL>\r\n                </service>\r\n            </serviceList>\r\n        </device>\r\n    </root>\r\n"
  },
  {
    "path": "package.json",
    "content": "{\r\n  \"name\": \"nodecast\",\r\n  \"main\": \"index.html\",\r\n  \"version\": \"0.0.1\",\r\n  \"private\": true,\r\n  \"dependencies\": {\r\n    \"peer-ssdp\": \"*\",\r\n    \"express\": \"*\",\r\n    \"node-uuid\": \"*\",\r\n    \"websocket\": \"*\",\r\n    \"querystring\": \"*\",\r\n    \"superagent\": \"*\",\r\n    \"optimist\": \"*\",\r\n    \"body-parser\": \"^1.3.0\",\r\n    \"morgan\": \"^1.1.1\",\r\n    \"method-override\": \"^2.0.2\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "public/css/main.css",
    "content": "html {\r\n\twidth: 100%;\r\n\theight: 100%;\r\n}\r\n\r\nbody {\r\n\twidth: 100%;\r\n\theight: 100%;\r\n\tmargin: 0;\r\n\tpadding: 0;\r\n    overflow: hidden;\r\n    background-color: #000000;\r\n}\r\n\r\niframe {\r\n\tborder: 0;\r\n}\r\n\r\n#idle, img.bg {\r\n        min-height: 100%;\r\n        min-width: 100%;\r\n        width: 100%;\r\n        height: auto;\r\n        position: fixed;\r\n        top: 0;\r\n        left: 0;\r\n\tz-index:-9999;\r\n}\r\n\r\n#idle_top {\r\n    position: absolute;\r\n    left: 0px;\r\n    right: 0px;\r\n    top: 0px;\r\n    bottom: 0px;\r\n    z-index: 101;\r\n    color: #ffffff;\r\n    font-size: 3em;\r\n    font-family: sans-serif;\r\n    padding: 40px;\r\n}\r\n\r\n#idle div {\r\n    -webkit-filter: blur(0px);\r\n}\r\n"
  },
  {
    "path": "public/index.html",
    "content": "<html>\r\n\t<head>\r\n\t<link rel=\"stylesheet\" href=\"css/main.css\">\r\n\t<script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-1.6.3.min.js\"></script>\r\n\t<script type=\"text/javascript\" src=\"js/jquery.cycle.all.2.74.js\"></script>\r\n        <script>\r\n\t$(document).ready(function() {\r\n\t\t\t\t$('#idle').cycle({\r\n\t\t\t\tfx: 'fade',\r\n\t\t\t\tpager: '#smallnav', \r\n\t\t\t\tpause:   1, \r\n\t\t\t\tspeed: 3600,\r\n\t\t\t\ttimeout:  3500 \r\n\t\t\t});\t\t\t\r\n\t\t});\r\n\r\n            var ws = new WebSocket(\"ws://localhost:8008/stage\");\r\n            var windows = {};\r\n\r\n            ws.onmessage = function (e) {\r\n                var cmd = $.parseJSON(e.data);\r\n\r\n                if(cmd.cmd == \"show\") {\r\n                    $('#idle_container').fadeOut('slow');\r\n\r\n                    var url = cmd.url;\r\n                    var windowName = \"popUp\";//$(this).attr(\"name\");\r\n\t\t    var windowSize = 'width='+screen.width;', height='+screen.height;\r\n\r\n                    windows[cmd.app] = window.open(url, windowName, windowSize);\r\n                   // window.location = url;\r\n                } else if(cmd.cmd == \"idle\") {\r\n                    $('#idle_container').fadeIn('slow');\r\n                } else if(cmd.cmd == \"close\") {\r\n                    $('#idle_container').fadeIn('slow');\r\n                    windows[cmd.app].close();\r\n                }\r\n            };\r\n        </script>\r\n\t</head>\r\n\t<body>\r\n        <div id=\"idle_container\" style=\"display: none;\">\r\n            <div id=\"idle\">\r\n\t\t<img src=\"img/background1.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background2.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background3.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background4.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background5.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background6.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background7.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background8.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background9.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background10.jpg\" class=\"bg\"/>\r\n\t\t<img src=\"img/background11.jpg\" class=\"bg\"/>\r\n            </div>\r\n            <div id=\"idle_top\">\r\n                <h3>Ready to cast.</h3>\r\n            </div>\r\n        </div>\r\n\t\t<iframe id=\"cast\" style=\"width: 100%; height:100%;\"/>\r\n\t</body>\r\n<html>\r\n"
  },
  {
    "path": "public/js/jquery.cycle.all.2.74.js",
    "content": "/*!\n * jQuery Cycle Plugin (with Transition Definitions)\n * Examples and documentation at: http://jquery.malsup.com/cycle/\n * Copyright (c) 2007-2009 M. Alsup\n * Version: 2.74 (03-FEB-2010)\n * Dual licensed under the MIT and GPL licenses:\n * http://www.opensource.org/licenses/mit-license.php\n * http://www.gnu.org/licenses/gpl.html\n * Requires: jQuery v1.2.6 or later\n */\n;(function($) {\n\nvar ver = '2.74';\n\n// if $.support is not defined (pre jQuery 1.3) add what I need\nif ($.support == undefined) {\n\t$.support = {\n\t\topacity: !($.browser.msie)\n\t};\n}\n\nfunction debug(s) {\n\tif ($.fn.cycle.debug)\n\t\tlog(s);\n}\t\t\nfunction log() {\n\tif (window.console && window.console.log)\n\t\twindow.console.log('[cycle] ' + Array.prototype.join.call(arguments,' '));\n};\n\n// the options arg can be...\n//   a number  - indicates an immediate transition should occur to the given slide index\n//   a string  - 'stop', 'pause', 'resume', 'next', 'prev', or the name of a transition effect (ie, 'fade', 'zoom', etc)\n//   an object - properties to control the slideshow\n//\n// the arg2 arg can be...\n//   the name of an fx (only used in conjunction with a numeric value for 'options')\n//   the value true (only used in first arg == 'resume') and indicates\n//\t that the resume should occur immediately (not wait for next timeout)\n\n$.fn.cycle = function(options, arg2) {\n\tvar o = { s: this.selector, c: this.context };\n\n\t// in 1.3+ we can fix mistakes with the ready state\n\tif (this.length === 0 && options != 'stop') {\n\t\tif (!$.isReady && o.s) {\n\t\t\tlog('DOM not ready, queuing slideshow');\n\t\t\t$(function() {\n\t\t\t\t$(o.s,o.c).cycle(options,arg2);\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\t\t// is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()\n\t\tlog('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));\n\t\treturn this;\n\t}\n\n\t// iterate the matched nodeset\n\treturn this.each(function() {\n\t\tvar opts = handleArguments(this, options, arg2);\n\t\tif (opts === false)\n\t\t\treturn;\n\n\t\t// stop existing slideshow for this container (if there is one)\n\t\tif (this.cycleTimeout)\n\t\t\tclearTimeout(this.cycleTimeout);\n\t\tthis.cycleTimeout = this.cyclePause = 0;\n\n\t\tvar $cont = $(this);\n\t\tvar $slides = opts.slideExpr ? $(opts.slideExpr, this) : $cont.children();\n\t\tvar els = $slides.get();\n\t\tif (els.length < 2) {\n\t\t\tlog('terminating; too few slides: ' + els.length);\n\t\t\treturn;\n\t\t}\n\n\t\tvar opts2 = buildOptions($cont, $slides, els, opts, o);\n\t\tif (opts2 === false)\n\t\t\treturn;\n\n\t\tvar startTime = opts2.continuous ? 10 : getTimeout(opts2.currSlide, opts2.nextSlide, opts2, !opts2.rev);\n\n\t\t// if it's an auto slideshow, kick it off\n\t\tif (startTime) {\n\t\t\tstartTime += (opts2.delay || 0);\n\t\t\tif (startTime < 10)\n\t\t\t\tstartTime = 10;\n\t\t\tdebug('first timeout: ' + startTime);\n\t\t\tthis.cycleTimeout = setTimeout(function(){go(els,opts2,0,!opts2.rev)}, startTime);\n\t\t}\n\t});\n};\n\n// process the args that were passed to the plugin fn\nfunction handleArguments(cont, options, arg2) {\n\tif (cont.cycleStop == undefined)\n\t\tcont.cycleStop = 0;\n\tif (options === undefined || options === null)\n\t\toptions = {};\n\tif (options.constructor == String) {\n\t\tswitch(options) {\n\t\tcase 'stop':\n\t\t\tcont.cycleStop++; // callbacks look for change\n\t\t\tif (cont.cycleTimeout)\n\t\t\t\tclearTimeout(cont.cycleTimeout);\n\t\t\tcont.cycleTimeout = 0;\n\t\t\t$(cont).removeData('cycle.opts');\n\t\t\treturn false;\n\t\tcase 'toggle':\n\t\t\tcont.cyclePause = (cont.cyclePause === 1) ? 0 : 1;\n\t\t\treturn false;\n\t\tcase 'pause':\n\t\t\tcont.cyclePause = 1;\n\t\t\treturn false;\n\t\tcase 'resume':\n\t\t\tcont.cyclePause = 0;\n\t\t\tif (arg2 === true) { // resume now!\n\t\t\t\toptions = $(cont).data('cycle.opts');\n\t\t\t\tif (!options) {\n\t\t\t\t\tlog('options not found, can not resume');\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (cont.cycleTimeout) {\n\t\t\t\t\tclearTimeout(cont.cycleTimeout);\n\t\t\t\t\tcont.cycleTimeout = 0;\n\t\t\t\t}\n\t\t\t\tgo(options.elements, options, 1, 1);\n\t\t\t}\n\t\t\treturn false;\n\t\tcase 'prev':\n\t\tcase 'next':\n\t\t\tvar opts = $(cont).data('cycle.opts');\n\t\t\tif (!opts) {\n\t\t\t\tlog('options not found, \"prev/next\" ignored');\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$.fn.cycle[options](opts);\n\t\t\treturn false;\n\t\tdefault:\n\t\t\toptions = { fx: options };\n\t\t};\n\t\treturn options;\n\t}\n\telse if (options.constructor == Number) {\n\t\t// go to the requested slide\n\t\tvar num = options;\n\t\toptions = $(cont).data('cycle.opts');\n\t\tif (!options) {\n\t\t\tlog('options not found, can not advance slide');\n\t\t\treturn false;\n\t\t}\n\t\tif (num < 0 || num >= options.elements.length) {\n\t\t\tlog('invalid slide index: ' + num);\n\t\t\treturn false;\n\t\t}\n\t\toptions.nextSlide = num;\n\t\tif (cont.cycleTimeout) {\n\t\t\tclearTimeout(cont.cycleTimeout);\n\t\t\tcont.cycleTimeout = 0;\n\t\t}\n\t\tif (typeof arg2 == 'string')\n\t\t\toptions.oneTimeFx = arg2;\n\t\tgo(options.elements, options, 1, num >= options.currSlide);\n\t\treturn false;\n\t}\n\treturn options;\n};\n\nfunction removeFilter(el, opts) {\n\tif (!$.support.opacity && opts.cleartype && el.style.filter) {\n\t\ttry { el.style.removeAttribute('filter'); }\n\t\tcatch(smother) {} // handle old opera versions\n\t}\n};\n\n// one-time initialization\nfunction buildOptions($cont, $slides, els, options, o) {\n\t// support metadata plugin (v1.0 and v2.0)\n\tvar opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {});\n\tif (opts.autostop)\n\t\topts.countdown = opts.autostopCount || els.length;\n\n\tvar cont = $cont[0];\n\t$cont.data('cycle.opts', opts);\n\topts.$cont = $cont;\n\topts.stopCount = cont.cycleStop;\n\topts.elements = els;\n\topts.before = opts.before ? [opts.before] : [];\n\topts.after = opts.after ? [opts.after] : [];\n\topts.after.unshift(function(){ opts.busy=0; });\n\n\t// push some after callbacks\n\tif (!$.support.opacity && opts.cleartype)\n\t\topts.after.push(function() { removeFilter(this, opts); });\n\tif (opts.continuous)\n\t\topts.after.push(function() { go(els,opts,0,!opts.rev); });\n\n\tsaveOriginalOpts(opts);\n\n\t// clearType corrections\n\tif (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)\n\t\tclearTypeFix($slides);\n\n\t// container requires non-static position so that slides can be position within\n\tif ($cont.css('position') == 'static')\n\t\t$cont.css('position', 'relative');\n\tif (opts.width)\n\t\t$cont.width(opts.width);\n\tif (opts.height && opts.height != 'auto')\n\t\t$cont.height(opts.height);\n\n\tif (opts.startingSlide)\n\t\topts.startingSlide = parseInt(opts.startingSlide);\n\n\t// if random, mix up the slide array\n\tif (opts.random) {\n\t\topts.randomMap = [];\n\t\tfor (var i = 0; i < els.length; i++)\n\t\t\topts.randomMap.push(i);\n\t\topts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});\n\t\topts.randomIndex = 0;\n\t\topts.startingSlide = opts.randomMap[0];\n\t}\n\telse if (opts.startingSlide >= els.length)\n\t\topts.startingSlide = 0; // catch bogus input\n\topts.currSlide = opts.startingSlide = opts.startingSlide || 0;\n\tvar first = opts.startingSlide;\n\n\t// set position and zIndex on all the slides\n\t$slides.css({position: 'absolute', top:0, left:0}).hide().each(function(i) {\n\t\tvar z = first ? i >= first ? els.length - (i-first) : first-i : els.length-i;\n\t\t$(this).css('z-index', z)\n\t});\n\n\t// make sure first slide is visible\n\t$(els[first]).css('opacity',1).show(); // opacity bit needed to handle restart use case\n\tremoveFilter(els[first], opts);\n\n\t// stretch slides\n\tif (opts.fit && opts.width)\n\t\t$slides.width(opts.width);\n\tif (opts.fit && opts.height && opts.height != 'auto')\n\t\t$slides.height(opts.height);\n\n\t// stretch container\n\tvar reshape = opts.containerResize && !$cont.innerHeight();\n\tif (reshape) { // do this only if container has no size http://tinyurl.com/da2oa9\n\t\tvar maxw = 0, maxh = 0;\n\t\tfor(var j=0; j < els.length; j++) {\n\t\t\tvar $e = $(els[j]), e = $e[0], w = $e.outerWidth(), h = $e.outerHeight();\n\t\t\tif (!w) w = e.offsetWidth;\n\t\t\tif (!h) h = e.offsetHeight;\n\t\t\tmaxw = w > maxw ? w : maxw;\n\t\t\tmaxh = h > maxh ? h : maxh;\n\t\t}\n\t\tif (maxw > 0 && maxh > 0)\n\t\t\t$cont.css({width:maxw+'px',height:maxh+'px'});\n\t}\n\n\tif (opts.pause)\n\t\t$cont.hover(function(){this.cyclePause++;},function(){this.cyclePause--;});\n\n\tif (supportMultiTransitions(opts) === false)\n\t\treturn false;\n\n\t// apparently a lot of people use image slideshows without height/width attributes on the images.\n\t// Cycle 2.50+ requires the sizing info for every slide; this block tries to deal with that.\n\tvar requeue = false;\n\toptions.requeueAttempts = options.requeueAttempts || 0;\n\t$slides.each(function() {\n\t\t// try to get height/width of each slide\n\t\tvar $el = $(this);\n\t\tthis.cycleH = (opts.fit && opts.height) ? opts.height : $el.height();\n\t\tthis.cycleW = (opts.fit && opts.width) ? opts.width : $el.width();\n\n\t\tif ( $el.is('img') ) {\n\t\t\t// sigh..  sniffing, hacking, shrugging...  this crappy hack tries to account for what browsers do when\n\t\t\t// an image is being downloaded and the markup did not include sizing info (height/width attributes);\n\t\t\t// there seems to be some \"default\" sizes used in this situation\n\t\t\tvar loadingIE\t= ($.browser.msie  && this.cycleW == 28 && this.cycleH == 30 && !this.complete);\n\t\t\tvar loadingFF\t= ($.browser.mozilla && this.cycleW == 34 && this.cycleH == 19 && !this.complete);\n\t\t\tvar loadingOp\t= ($.browser.opera && ((this.cycleW == 42 && this.cycleH == 19) || (this.cycleW == 37 && this.cycleH == 17)) && !this.complete);\n\t\t\tvar loadingOther = (this.cycleH == 0 && this.cycleW == 0 && !this.complete);\n\t\t\t// don't requeue for images that are still loading but have a valid size\n\t\t\tif (loadingIE || loadingFF || loadingOp || loadingOther) {\n\t\t\t\tif (o.s && opts.requeueOnImageNotLoaded && ++options.requeueAttempts < 100) { // track retry count so we don't loop forever\n\t\t\t\t\tlog(options.requeueAttempts,' - img slide not loaded, requeuing slideshow: ', this.src, this.cycleW, this.cycleH);\n\t\t\t\t\tsetTimeout(function() {$(o.s,o.c).cycle(options)}, opts.requeueTimeout);\n\t\t\t\t\trequeue = true;\n\t\t\t\t\treturn false; // break each loop\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlog('could not determine size of image: '+this.src, this.cycleW, this.cycleH);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t});\n\n\tif (requeue)\n\t\treturn false;\n\n\topts.cssBefore = opts.cssBefore || {};\n\topts.animIn = opts.animIn || {};\n\topts.animOut = opts.animOut || {};\n\n\t$slides.not(':eq('+first+')').css(opts.cssBefore);\n\tif (opts.cssFirst)\n\t\t$($slides[first]).css(opts.cssFirst);\n\n\tif (opts.timeout) {\n\t\topts.timeout = parseInt(opts.timeout);\n\t\t// ensure that timeout and speed settings are sane\n\t\tif (opts.speed.constructor == String)\n\t\t\topts.speed = $.fx.speeds[opts.speed] || parseInt(opts.speed);\n\t\tif (!opts.sync)\n\t\t\topts.speed = opts.speed / 2;\n\t\twhile((opts.timeout - opts.speed) < 250) // sanitize timeout\n\t\t\topts.timeout += opts.speed;\n\t}\n\tif (opts.easing)\n\t\topts.easeIn = opts.easeOut = opts.easing;\n\tif (!opts.speedIn)\n\t\topts.speedIn = opts.speed;\n\tif (!opts.speedOut)\n\t\topts.speedOut = opts.speed;\n\n\topts.slideCount = els.length;\n\topts.currSlide = opts.lastSlide = first;\n\tif (opts.random) {\n\t\topts.nextSlide = opts.currSlide;\n\t\tif (++opts.randomIndex == els.length)\n\t\t\topts.randomIndex = 0;\n\t\topts.nextSlide = opts.randomMap[opts.randomIndex];\n\t}\n\telse\n\t\topts.nextSlide = opts.startingSlide >= (els.length-1) ? 0 : opts.startingSlide+1;\n\n\t// run transition init fn\n\tif (!opts.multiFx) {\n\t\tvar init = $.fn.cycle.transitions[opts.fx];\n\t\tif ($.isFunction(init))\n\t\t\tinit($cont, $slides, opts);\n\t\telse if (opts.fx != 'custom' && !opts.multiFx) {\n\t\t\tlog('unknown transition: ' + opts.fx,'; slideshow terminating');\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// fire artificial events\n\tvar e0 = $slides[first];\n\tif (opts.before.length)\n\t\topts.before[0].apply(e0, [e0, e0, opts, true]);\n\tif (opts.after.length > 1)\n\t\topts.after[1].apply(e0, [e0, e0, opts, true]);\n\n\tif (opts.next)\n\t\t$(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,opts.rev?-1:1)});\n\tif (opts.prev)\n\t\t$(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,opts.rev?1:-1)});\n\tif (opts.pager)\n\t\tbuildPager(els,opts);\n\n\texposeAddSlide(opts, els);\n\n\treturn opts;\n};\n\n// save off original opts so we can restore after clearing state\nfunction saveOriginalOpts(opts) {\n\topts.original = { before: [], after: [] };\n\topts.original.cssBefore = $.extend({}, opts.cssBefore);\n\topts.original.cssAfter  = $.extend({}, opts.cssAfter);\n\topts.original.animIn\t= $.extend({}, opts.animIn);\n\topts.original.animOut   = $.extend({}, opts.animOut);\n\t$.each(opts.before, function() { opts.original.before.push(this); });\n\t$.each(opts.after,  function() { opts.original.after.push(this); });\n};\n\nfunction supportMultiTransitions(opts) {\n\tvar i, tx, txs = $.fn.cycle.transitions;\n\t// look for multiple effects\n\tif (opts.fx.indexOf(',') > 0) {\n\t\topts.multiFx = true;\n\t\topts.fxs = opts.fx.replace(/\\s*/g,'').split(',');\n\t\t// discard any bogus effect names\n\t\tfor (i=0; i < opts.fxs.length; i++) {\n\t\t\tvar fx = opts.fxs[i];\n\t\t\ttx = txs[fx];\n\t\t\tif (!tx || !txs.hasOwnProperty(fx) || !$.isFunction(tx)) {\n\t\t\t\tlog('discarding unknown transition: ',fx);\n\t\t\t\topts.fxs.splice(i,1);\n\t\t\t\ti--;\n\t\t\t}\n\t\t}\n\t\t// if we have an empty list then we threw everything away!\n\t\tif (!opts.fxs.length) {\n\t\t\tlog('No valid transitions named; slideshow terminating.');\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (opts.fx == 'all') {  // auto-gen the list of transitions\n\t\topts.multiFx = true;\n\t\topts.fxs = [];\n\t\tfor (p in txs) {\n\t\t\ttx = txs[p];\n\t\t\tif (txs.hasOwnProperty(p) && $.isFunction(tx))\n\t\t\t\topts.fxs.push(p);\n\t\t}\n\t}\n\tif (opts.multiFx && opts.randomizeEffects) {\n\t\t// munge the fxs array to make effect selection random\n\t\tvar r1 = Math.floor(Math.random() * 20) + 30;\n\t\tfor (i = 0; i < r1; i++) {\n\t\t\tvar r2 = Math.floor(Math.random() * opts.fxs.length);\n\t\t\topts.fxs.push(opts.fxs.splice(r2,1)[0]);\n\t\t}\n\t\tdebug('randomized fx sequence: ',opts.fxs);\n\t}\n\treturn true;\n};\n\n// provide a mechanism for adding slides after the slideshow has started\nfunction exposeAddSlide(opts, els) {\n\topts.addSlide = function(newSlide, prepend) {\n\t\tvar $s = $(newSlide), s = $s[0];\n\t\tif (!opts.autostopCount)\n\t\t\topts.countdown++;\n\t\tels[prepend?'unshift':'push'](s);\n\t\tif (opts.els)\n\t\t\topts.els[prepend?'unshift':'push'](s); // shuffle needs this\n\t\topts.slideCount = els.length;\n\n\t\t$s.css('position','absolute');\n\t\t$s[prepend?'prependTo':'appendTo'](opts.$cont);\n\n\t\tif (prepend) {\n\t\t\topts.currSlide++;\n\t\t\topts.nextSlide++;\n\t\t}\n\n\t\tif (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)\n\t\t\tclearTypeFix($s);\n\n\t\tif (opts.fit && opts.width)\n\t\t\t$s.width(opts.width);\n\t\tif (opts.fit && opts.height && opts.height != 'auto')\n\t\t\t$slides.height(opts.height);\n\t\ts.cycleH = (opts.fit && opts.height) ? opts.height : $s.height();\n\t\ts.cycleW = (opts.fit && opts.width) ? opts.width : $s.width();\n\n\t\t$s.css(opts.cssBefore);\n\n\t\tif (opts.pager)\n\t\t\t$.fn.cycle.createPagerAnchor(els.length-1, s, $(opts.pager), els, opts);\n\n\t\tif ($.isFunction(opts.onAddSlide))\n\t\t\topts.onAddSlide($s);\n\t\telse\n\t\t\t$s.hide(); // default behavior\n\t};\n}\n\n// reset internal state; we do this on every pass in order to support multiple effects\n$.fn.cycle.resetState = function(opts, fx) {\n\tfx = fx || opts.fx;\n\topts.before = []; opts.after = [];\n\topts.cssBefore = $.extend({}, opts.original.cssBefore);\n\topts.cssAfter  = $.extend({}, opts.original.cssAfter);\n\topts.animIn\t= $.extend({}, opts.original.animIn);\n\topts.animOut   = $.extend({}, opts.original.animOut);\n\topts.fxFn = null;\n\t$.each(opts.original.before, function() { opts.before.push(this); });\n\t$.each(opts.original.after,  function() { opts.after.push(this); });\n\n\t// re-init\n\tvar init = $.fn.cycle.transitions[fx];\n\tif ($.isFunction(init))\n\t\tinit(opts.$cont, $(opts.elements), opts);\n};\n\n// this is the main engine fn, it handles the timeouts, callbacks and slide index mgmt\nfunction go(els, opts, manual, fwd) {\n\t// opts.busy is true if we're in the middle of an animation\n\tif (manual && opts.busy && opts.manualTrump) {\n\t\t// let manual transitions requests trump active ones\n\t\t$(els).stop(true,true);\n\t\topts.busy = false;\n\t}\n\t// don't begin another timeout-based transition if there is one active\n\tif (opts.busy)\n\t\treturn;\n\n\tvar p = opts.$cont[0], curr = els[opts.currSlide], next = els[opts.nextSlide];\n\n\t// stop cycling if we have an outstanding stop request\n\tif (p.cycleStop != opts.stopCount || p.cycleTimeout === 0 && !manual)\n\t\treturn;\n\n\t// check to see if we should stop cycling based on autostop options\n\tif (!manual && !p.cyclePause &&\n\t\t((opts.autostop && (--opts.countdown <= 0)) ||\n\t\t(opts.nowrap && !opts.random && opts.nextSlide < opts.currSlide))) {\n\t\tif (opts.end)\n\t\t\topts.end(opts);\n\t\treturn;\n\t}\n\n\t// if slideshow is paused, only transition on a manual trigger\n\tif (manual || !p.cyclePause) {\n\t\tvar fx = opts.fx;\n\t\t// keep trying to get the slide size if we don't have it yet\n\t\tcurr.cycleH = curr.cycleH || $(curr).height();\n\t\tcurr.cycleW = curr.cycleW || $(curr).width();\n\t\tnext.cycleH = next.cycleH || $(next).height();\n\t\tnext.cycleW = next.cycleW || $(next).width();\n\n\t\t// support multiple transition types\n\t\tif (opts.multiFx) {\n\t\t\tif (opts.lastFx == undefined || ++opts.lastFx >= opts.fxs.length)\n\t\t\t\topts.lastFx = 0;\n\t\t\tfx = opts.fxs[opts.lastFx];\n\t\t\topts.currFx = fx;\n\t\t}\n\n\t\t// one-time fx overrides apply to:  $('div').cycle(3,'zoom');\n\t\tif (opts.oneTimeFx) {\n\t\t\tfx = opts.oneTimeFx;\n\t\t\topts.oneTimeFx = null;\n\t\t}\n\n\t\t$.fn.cycle.resetState(opts, fx);\n\n\t\t// run the before callbacks\n\t\tif (opts.before.length)\n\t\t\t$.each(opts.before, function(i,o) {\n\t\t\t\tif (p.cycleStop != opts.stopCount) return;\n\t\t\t\to.apply(next, [curr, next, opts, fwd]);\n\t\t\t});\n\n\t\t// stage the after callacks\n\t\tvar after = function() {\n\t\t\t$.each(opts.after, function(i,o) {\n\t\t\t\tif (p.cycleStop != opts.stopCount) return;\n\t\t\t\to.apply(next, [curr, next, opts, fwd]);\n\t\t\t});\n\t\t};\n\n\t\tif (opts.nextSlide != opts.currSlide) {\n\t\t\t// get ready to perform the transition\n\t\t\topts.busy = 1;\n\t\t\tif (opts.fxFn) // fx function provided?\n\t\t\t\topts.fxFn(curr, next, opts, after, fwd);\n\t\t\telse if ($.isFunction($.fn.cycle[opts.fx])) // fx plugin ?\n\t\t\t\t$.fn.cycle[opts.fx](curr, next, opts, after);\n\t\t\telse\n\t\t\t\t$.fn.cycle.custom(curr, next, opts, after, manual && opts.fastOnEvent);\n\t\t}\n\n\t\t// calculate the next slide\n\t\topts.lastSlide = opts.currSlide;\n\t\tif (opts.random) {\n\t\t\topts.currSlide = opts.nextSlide;\n\t\t\tif (++opts.randomIndex == els.length)\n\t\t\t\topts.randomIndex = 0;\n\t\t\topts.nextSlide = opts.randomMap[opts.randomIndex];\n\t\t}\n\t\telse { // sequence\n\t\t\tvar roll = (opts.nextSlide + 1) == els.length;\n\t\t\topts.nextSlide = roll ? 0 : opts.nextSlide+1;\n\t\t\topts.currSlide = roll ? els.length-1 : opts.nextSlide-1;\n\t\t}\n\n\t\tif (opts.pager)\n\t\t\t$.fn.cycle.updateActivePagerLink(opts.pager, opts.currSlide);\n\t}\n\n\t// stage the next transition\n\tvar ms = 0;\n\tif (opts.timeout && !opts.continuous)\n\t\tms = getTimeout(curr, next, opts, fwd);\n\telse if (opts.continuous && p.cyclePause) // continuous shows work off an after callback, not this timer logic\n\t\tms = 10;\n\tif (ms > 0)\n\t\tp.cycleTimeout = setTimeout(function(){ go(els, opts, 0, !opts.rev) }, ms);\n};\n\n// invoked after transition\n$.fn.cycle.updateActivePagerLink = function(pager, currSlide) {\n\t$(pager).each(function() {\n\t\t$(this).find('a').removeClass('activeSlide').filter('a:eq('+currSlide+')').addClass('activeSlide');\n\t});\n};\n\n// calculate timeout value for current transition\nfunction getTimeout(curr, next, opts, fwd) {\n\tif (opts.timeoutFn) {\n\t\t// call user provided calc fn\n\t\tvar t = opts.timeoutFn(curr,next,opts,fwd);\n\t\twhile ((t - opts.speed) < 250) // sanitize timeout\n\t\t\tt += opts.speed;\n\t\tdebug('calculated timeout: ' + t + '; speed: ' + opts.speed);\n\t\tif (t !== false)\n\t\t\treturn t;\n\t}\n\treturn opts.timeout;\n};\n\n// expose next/prev function, caller must pass in state\n$.fn.cycle.next = function(opts) { advance(opts, opts.rev?-1:1); };\n$.fn.cycle.prev = function(opts) { advance(opts, opts.rev?1:-1);};\n\n// advance slide forward or back\nfunction advance(opts, val) {\n\tvar els = opts.elements;\n\tvar p = opts.$cont[0], timeout = p.cycleTimeout;\n\tif (timeout) {\n\t\tclearTimeout(timeout);\n\t\tp.cycleTimeout = 0;\n\t}\n\tif (opts.random && val < 0) {\n\t\t// move back to the previously display slide\n\t\topts.randomIndex--;\n\t\tif (--opts.randomIndex == -2)\n\t\t\topts.randomIndex = els.length-2;\n\t\telse if (opts.randomIndex == -1)\n\t\t\topts.randomIndex = els.length-1;\n\t\topts.nextSlide = opts.randomMap[opts.randomIndex];\n\t}\n\telse if (opts.random) {\n\t\tif (++opts.randomIndex == els.length)\n\t\t\topts.randomIndex = 0;\n\t\topts.nextSlide = opts.randomMap[opts.randomIndex];\n\t}\n\telse {\n\t\topts.nextSlide = opts.currSlide + val;\n\t\tif (opts.nextSlide < 0) {\n\t\t\tif (opts.nowrap) return false;\n\t\t\topts.nextSlide = els.length - 1;\n\t\t}\n\t\telse if (opts.nextSlide >= els.length) {\n\t\t\tif (opts.nowrap) return false;\n\t\t\topts.nextSlide = 0;\n\t\t}\n\t}\n\n\tif ($.isFunction(opts.prevNextClick))\n\t\topts.prevNextClick(val > 0, opts.nextSlide, els[opts.nextSlide]);\n\tgo(els, opts, 1, val>=0);\n\treturn false;\n};\n\nfunction buildPager(els, opts) {\n\tvar $p = $(opts.pager);\n\t$.each(els, function(i,o) {\n\t\t$.fn.cycle.createPagerAnchor(i,o,$p,els,opts);\n\t});\n   $.fn.cycle.updateActivePagerLink(opts.pager, opts.startingSlide);\n};\n\n$.fn.cycle.createPagerAnchor = function(i, el, $p, els, opts) {\n\tvar a;\n\tif ($.isFunction(opts.pagerAnchorBuilder))\n\t\ta = opts.pagerAnchorBuilder(i,el);\n\telse\n\t\ta = '<a href=\"#\">'+(i+1)+'</a>';\n\t\t\n\tif (!a)\n\t\treturn;\n\tvar $a = $(a);\n\t// don't reparent if anchor is in the dom\n\tif ($a.parents('body').length === 0) {\n\t\tvar arr = [];\n\t\tif ($p.length > 1) {\n\t\t\t$p.each(function() {\n\t\t\t\tvar $clone = $a.clone(true);\n\t\t\t\t$(this).append($clone);\n\t\t\t\tarr.push($clone[0]);\n\t\t\t});\n\t\t\t$a = $(arr);\n\t\t}\n\t\telse {\n\t\t\t$a.appendTo($p);\n\t\t}\n\t}\n\n\t$a.bind(opts.pagerEvent, function(e) {\n\t\te.preventDefault();\n\t\topts.nextSlide = i;\n\t\tvar p = opts.$cont[0], timeout = p.cycleTimeout;\n\t\tif (timeout) {\n\t\t\tclearTimeout(timeout);\n\t\t\tp.cycleTimeout = 0;\n\t\t}\n\t\tif ($.isFunction(opts.pagerClick))\n\t\t\topts.pagerClick(opts.nextSlide, els[opts.nextSlide]);\n\t\tgo(els,opts,1,opts.currSlide < i); // trigger the trans\n\t\treturn false;\n\t});\n\t\n\tif (opts.pagerEvent != 'click')\n\t\t$a.click(function(){return false;}); // supress click\n\t\n\tif (opts.pauseOnPagerHover)\n\t\t$a.hover(function() { opts.$cont[0].cyclePause++; }, function() { opts.$cont[0].cyclePause--; } );\n};\n\n// helper fn to calculate the number of slides between the current and the next\n$.fn.cycle.hopsFromLast = function(opts, fwd) {\n\tvar hops, l = opts.lastSlide, c = opts.currSlide;\n\tif (fwd)\n\t\thops = c > l ? c - l : opts.slideCount - l;\n\telse\n\t\thops = c < l ? l - c : l + opts.slideCount - c;\n\treturn hops;\n};\n\n// fix clearType problems in ie6 by setting an explicit bg color\n// (otherwise text slides look horrible during a fade transition)\nfunction clearTypeFix($slides) {\n\tfunction hex(s) {\n\t\ts = parseInt(s).toString(16);\n\t\treturn s.length < 2 ? '0'+s : s;\n\t};\n\tfunction getBg(e) {\n\t\tfor ( ; e && e.nodeName.toLowerCase() != 'html'; e = e.parentNode) {\n\t\t\tvar v = $.css(e,'background-color');\n\t\t\tif (v.indexOf('rgb') >= 0 ) {\n\t\t\t\tvar rgb = v.match(/\\d+/g);\n\t\t\t\treturn '#'+ hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);\n\t\t\t}\n\t\t\tif (v && v != 'transparent')\n\t\t\t\treturn v;\n\t\t}\n\t\treturn '#ffffff';\n\t};\n\t$slides.each(function() { $(this).css('background-color', getBg(this)); });\n};\n\n// reset common props before the next transition\n$.fn.cycle.commonReset = function(curr,next,opts,w,h,rev) {\n\t$(opts.elements).not(curr).hide();\n\topts.cssBefore.opacity = 1;\n\topts.cssBefore.display = 'block';\n\tif (w !== false && next.cycleW > 0)\n\t\topts.cssBefore.width = next.cycleW;\n\tif (h !== false && next.cycleH > 0)\n\t\topts.cssBefore.height = next.cycleH;\n\topts.cssAfter = opts.cssAfter || {};\n\topts.cssAfter.display = 'none';\n\t$(curr).css('zIndex',opts.slideCount + (rev === true ? 1 : 0));\n\t$(next).css('zIndex',opts.slideCount + (rev === true ? 0 : 1));\n};\n\n// the actual fn for effecting a transition\n$.fn.cycle.custom = function(curr, next, opts, cb, speedOverride) {\n\tvar $l = $(curr), $n = $(next);\n\tvar speedIn = opts.speedIn, speedOut = opts.speedOut, easeIn = opts.easeIn, easeOut = opts.easeOut;\n\t$n.css(opts.cssBefore);\n\tif (speedOverride) {\n\t\tif (typeof speedOverride == 'number')\n\t\t\tspeedIn = speedOut = speedOverride;\n\t\telse\n\t\t\tspeedIn = speedOut = 1;\n\t\teaseIn = easeOut = null;\n\t}\n\tvar fn = function() {$n.animate(opts.animIn, speedIn, easeIn, cb)};\n\t$l.animate(opts.animOut, speedOut, easeOut, function() {\n\t\tif (opts.cssAfter) $l.css(opts.cssAfter);\n\t\tif (!opts.sync) fn();\n\t});\n\tif (opts.sync) fn();\n};\n\n// transition definitions - only fade is defined here, transition pack defines the rest\n$.fn.cycle.transitions = {\n\tfade: function($cont, $slides, opts) {\n\t\t$slides.not(':eq('+opts.currSlide+')').css('opacity',0);\n\t\topts.before.push(function(curr,next,opts) {\n\t\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\t\topts.cssBefore.opacity = 0;\n\t\t});\n\t\topts.animIn\t   = { opacity: 1 };\n\t\topts.animOut   = { opacity: 0 };\n\t\topts.cssBefore = { top: 0, left: 0 };\n\t}\n};\n\n$.fn.cycle.ver = function() { return ver; };\n\n// override these globally if you like (they are all optional)\n$.fn.cycle.defaults = {\n\tfx:\t\t\t  'fade', // name of transition effect (or comma separated names, ex: fade,scrollUp,shuffle)\n\ttimeout:\t   4000,  // milliseconds between slide transitions (0 to disable auto advance)\n\ttimeoutFn:     null,  // callback for determining per-slide timeout value:  function(currSlideElement, nextSlideElement, options, forwardFlag)\n\tcontinuous:\t   0,\t  // true to start next transition immediately after current one completes\n\tspeed:\t\t   1000,  // speed of the transition (any valid fx speed value)\n\tspeedIn:\t   null,  // speed of the 'in' transition\n\tspeedOut:\t   null,  // speed of the 'out' transition\n\tnext:\t\t   null,  // selector for element to use as click trigger for next slide\n\tprev:\t\t   null,  // selector for element to use as click trigger for previous slide\n\tprevNextClick: null,  // callback fn for prev/next clicks:\tfunction(isNext, zeroBasedSlideIndex, slideElement)\n\tprevNextEvent:'click',// event which drives the manual transition to the previous or next slide\n\tpager:\t\t   null,  // selector for element to use as pager container\n\tpagerClick:\t   null,  // callback fn for pager clicks:\tfunction(zeroBasedSlideIndex, slideElement)\n\tpagerEvent:\t  'click', // name of event which drives the pager navigation\n\tpagerAnchorBuilder: null, // callback fn for building anchor links:  function(index, DOMelement)\n\tbefore:\t\t   null,  // transition callback (scope set to element to be shown):\t function(currSlideElement, nextSlideElement, options, forwardFlag)\n\tafter:\t\t   null,  // transition callback (scope set to element that was shown):  function(currSlideElement, nextSlideElement, options, forwardFlag)\n\tend:\t\t   null,  // callback invoked when the slideshow terminates (use with autostop or nowrap options): function(options)\n\teasing:\t\t   null,  // easing method for both in and out transitions\n\teaseIn:\t\t   null,  // easing for \"in\" transition\n\teaseOut:\t   null,  // easing for \"out\" transition\n\tshuffle:\t   null,  // coords for shuffle animation, ex: { top:15, left: 200 }\n\tanimIn:\t\t   null,  // properties that define how the slide animates in\n\tanimOut:\t   null,  // properties that define how the slide animates out\n\tcssBefore:\t   null,  // properties that define the initial state of the slide before transitioning in\n\tcssAfter:\t   null,  // properties that defined the state of the slide after transitioning out\n\tfxFn:\t\t   null,  // function used to control the transition: function(currSlideElement, nextSlideElement, options, afterCalback, forwardFlag)\n\theight:\t\t  'auto', // container height\n\tstartingSlide: 0,\t  // zero-based index of the first slide to be displayed\n\tsync:\t\t   1,\t  // true if in/out transitions should occur simultaneously\n\trandom:\t\t   0,\t  // true for random, false for sequence (not applicable to shuffle fx)\n\tfit:\t\t   0,\t  // force slides to fit container\n\tcontainerResize: 1,\t  // resize container to fit largest slide\n\tpause:\t\t   0,\t  // true to enable \"pause on hover\"\n\tpauseOnPagerHover: 0, // true to pause when hovering over pager link\n\tautostop:\t   0,\t  // true to end slideshow after X transitions (where X == slide count)\n\tautostopCount: 0,\t  // number of transitions (optionally used with autostop to define X)\n\tdelay:\t\t   0,\t  // additional delay (in ms) for first transition (hint: can be negative)\n\tslideExpr:\t   null,  // expression for selecting slides (if something other than all children is required)\n\tcleartype:\t   !$.support.opacity,  // true if clearType corrections should be applied (for IE)\n\tcleartypeNoBg: false, // set to true to disable extra cleartype fixing (leave false to force background color setting on slides)\n\tnowrap:\t\t   0,\t  // true to prevent slideshow from wrapping\n\tfastOnEvent:   0,\t  // force fast transitions when triggered manually (via pager or prev/next); value == time in ms\n\trandomizeEffects: 1,  // valid when multiple effects are used; true to make the effect sequence random\n\trev:\t\t   0,\t // causes animations to transition in reverse\n\tmanualTrump:   true,  // causes manual transition to stop an active transition instead of being ignored\n\trequeueOnImageNotLoaded: true, // requeue the slideshow if any image slides are not yet loaded\n\trequeueTimeout: 250   // ms delay for requeue\n};\n\n})(jQuery);\n\n\n/*!\n * jQuery Cycle Plugin Transition Definitions\n * This script is a plugin for the jQuery Cycle Plugin\n * Examples and documentation at: http://malsup.com/jquery/cycle/\n * Copyright (c) 2007-2008 M. Alsup\n * Version:\t 2.72\n * Dual licensed under the MIT and GPL licenses:\n * http://www.opensource.org/licenses/mit-license.php\n * http://www.gnu.org/licenses/gpl.html\n */\n(function($) {\n\n//\n// These functions define one-time slide initialization for the named\n// transitions. To save file size feel free to remove any of these that you\n// don't need.\n//\n$.fn.cycle.transitions.none = function($cont, $slides, opts) {\n\topts.fxFn = function(curr,next,opts,after){\n\t\t$(next).show();\n\t\t$(curr).hide();\n\t\tafter();\n\t};\n}\n\n// scrollUp/Down/Left/Right\n$.fn.cycle.transitions.scrollUp = function($cont, $slides, opts) {\n\t$cont.css('overflow','hidden');\n\topts.before.push($.fn.cycle.commonReset);\n\tvar h = $cont.height();\n\topts.cssBefore ={ top: h, left: 0 };\n\topts.cssFirst = { top: 0 };\n\topts.animIn\t  = { top: 0 };\n\topts.animOut  = { top: -h };\n};\n$.fn.cycle.transitions.scrollDown = function($cont, $slides, opts) {\n\t$cont.css('overflow','hidden');\n\topts.before.push($.fn.cycle.commonReset);\n\tvar h = $cont.height();\n\topts.cssFirst = { top: 0 };\n\topts.cssBefore= { top: -h, left: 0 };\n\topts.animIn\t  = { top: 0 };\n\topts.animOut  = { top: h };\n};\n$.fn.cycle.transitions.scrollLeft = function($cont, $slides, opts) {\n\t$cont.css('overflow','hidden');\n\topts.before.push($.fn.cycle.commonReset);\n\tvar w = $cont.width();\n\topts.cssFirst = { left: 0 };\n\topts.cssBefore= { left: w, top: 0 };\n\topts.animIn\t  = { left: 0 };\n\topts.animOut  = { left: 0-w };\n};\n$.fn.cycle.transitions.scrollRight = function($cont, $slides, opts) {\n\t$cont.css('overflow','hidden');\n\topts.before.push($.fn.cycle.commonReset);\n\tvar w = $cont.width();\n\topts.cssFirst = { left: 0 };\n\topts.cssBefore= { left: -w, top: 0 };\n\topts.animIn\t  = { left: 0 };\n\topts.animOut  = { left: w };\n};\n$.fn.cycle.transitions.scrollHorz = function($cont, $slides, opts) {\n\t$cont.css('overflow','hidden').width();\n\topts.before.push(function(curr, next, opts, fwd) {\n\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\topts.cssBefore.left = fwd ? (next.cycleW-1) : (1-next.cycleW);\n\t\topts.animOut.left = fwd ? -curr.cycleW : curr.cycleW;\n\t});\n\topts.cssFirst = { left: 0 };\n\topts.cssBefore= { top: 0 };\n\topts.animIn   = { left: 0 };\n\topts.animOut  = { top: 0 };\n};\n$.fn.cycle.transitions.scrollVert = function($cont, $slides, opts) {\n\t$cont.css('overflow','hidden');\n\topts.before.push(function(curr, next, opts, fwd) {\n\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\topts.cssBefore.top = fwd ? (1-next.cycleH) : (next.cycleH-1);\n\t\topts.animOut.top = fwd ? curr.cycleH : -curr.cycleH;\n\t});\n\topts.cssFirst = { top: 0 };\n\topts.cssBefore= { left: 0 };\n\topts.animIn   = { top: 0 };\n\topts.animOut  = { left: 0 };\n};\n\n// slideX/slideY\n$.fn.cycle.transitions.slideX = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$(opts.elements).not(curr).hide();\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,true);\n\t\topts.animIn.width = next.cycleW;\n\t});\n\topts.cssBefore = { left: 0, top: 0, width: 0 };\n\topts.animIn\t = { width: 'show' };\n\topts.animOut = { width: 0 };\n};\n$.fn.cycle.transitions.slideY = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$(opts.elements).not(curr).hide();\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,false);\n\t\topts.animIn.height = next.cycleH;\n\t});\n\topts.cssBefore = { left: 0, top: 0, height: 0 };\n\topts.animIn\t = { height: 'show' };\n\topts.animOut = { height: 0 };\n};\n\n// shuffle\n$.fn.cycle.transitions.shuffle = function($cont, $slides, opts) {\n\tvar i, w = $cont.css('overflow', 'visible').width();\n\t$slides.css({left: 0, top: 0});\n\topts.before.push(function(curr,next,opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,true,true);\n\t});\n\t// only adjust speed once!\n\tif (!opts.speedAdjusted) {\n\t\topts.speed = opts.speed / 2; // shuffle has 2 transitions\n\t\topts.speedAdjusted = true;\n\t}\n\topts.random = 0;\n\topts.shuffle = opts.shuffle || {left:-w, top:15};\n\topts.els = [];\n\tfor (i=0; i < $slides.length; i++)\n\t\topts.els.push($slides[i]);\n\n\tfor (i=0; i < opts.currSlide; i++)\n\t\topts.els.push(opts.els.shift());\n\n\t// custom transition fn (hat tip to Benjamin Sterling for this bit of sweetness!)\n\topts.fxFn = function(curr, next, opts, cb, fwd) {\n\t\tvar $el = fwd ? $(curr) : $(next);\n\t\t$(next).css(opts.cssBefore);\n\t\tvar count = opts.slideCount;\n\t\t$el.animate(opts.shuffle, opts.speedIn, opts.easeIn, function() {\n\t\t\tvar hops = $.fn.cycle.hopsFromLast(opts, fwd);\n\t\t\tfor (var k=0; k < hops; k++)\n\t\t\t\tfwd ? opts.els.push(opts.els.shift()) : opts.els.unshift(opts.els.pop());\n\t\t\tif (fwd) {\n\t\t\t\tfor (var i=0, len=opts.els.length; i < len; i++)\n\t\t\t\t\t$(opts.els[i]).css('z-index', len-i+count);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvar z = $(curr).css('z-index');\n\t\t\t\t$el.css('z-index', parseInt(z)+1+count);\n\t\t\t}\n\t\t\t$el.animate({left:0, top:0}, opts.speedOut, opts.easeOut, function() {\n\t\t\t\t$(fwd ? this : curr).hide();\n\t\t\t\tif (cb) cb();\n\t\t\t});\n\t\t});\n\t};\n\topts.cssBefore = { display: 'block', opacity: 1, top: 0, left: 0 };\n};\n\n// turnUp/Down/Left/Right\n$.fn.cycle.transitions.turnUp = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,false);\n\t\topts.cssBefore.top = next.cycleH;\n\t\topts.animIn.height = next.cycleH;\n\t});\n\topts.cssFirst  = { top: 0 };\n\topts.cssBefore = { left: 0, height: 0 };\n\topts.animIn\t   = { top: 0 };\n\topts.animOut   = { height: 0 };\n};\n$.fn.cycle.transitions.turnDown = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,false);\n\t\topts.animIn.height = next.cycleH;\n\t\topts.animOut.top   = curr.cycleH;\n\t});\n\topts.cssFirst  = { top: 0 };\n\topts.cssBefore = { left: 0, top: 0, height: 0 };\n\topts.animOut   = { height: 0 };\n};\n$.fn.cycle.transitions.turnLeft = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,true);\n\t\topts.cssBefore.left = next.cycleW;\n\t\topts.animIn.width = next.cycleW;\n\t});\n\topts.cssBefore = { top: 0, width: 0  };\n\topts.animIn\t   = { left: 0 };\n\topts.animOut   = { width: 0 };\n};\n$.fn.cycle.transitions.turnRight = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,true);\n\t\topts.animIn.width = next.cycleW;\n\t\topts.animOut.left = curr.cycleW;\n\t});\n\topts.cssBefore = { top: 0, left: 0, width: 0 };\n\topts.animIn\t   = { left: 0 };\n\topts.animOut   = { width: 0 };\n};\n\n// zoom\n$.fn.cycle.transitions.zoom = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,false,true);\n\t\topts.cssBefore.top = next.cycleH/2;\n\t\topts.cssBefore.left = next.cycleW/2;\n\t\topts.animIn\t   = { top: 0, left: 0, width: next.cycleW, height: next.cycleH };\n\t\topts.animOut   = { width: 0, height: 0, top: curr.cycleH/2, left: curr.cycleW/2 };\n\t});\n\topts.cssFirst = { top:0, left: 0 };\n\topts.cssBefore = { width: 0, height: 0 };\n};\n\n// fadeZoom\n$.fn.cycle.transitions.fadeZoom = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,false);\n\t\topts.cssBefore.left = next.cycleW/2;\n\t\topts.cssBefore.top = next.cycleH/2;\n\t\topts.animIn\t= { top: 0, left: 0, width: next.cycleW, height: next.cycleH };\n\t});\n\topts.cssBefore = { width: 0, height: 0 };\n\topts.animOut  = { opacity: 0 };\n};\n\n// blindX\n$.fn.cycle.transitions.blindX = function($cont, $slides, opts) {\n\tvar w = $cont.css('overflow','hidden').width();\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\topts.animIn.width = next.cycleW;\n\t\topts.animOut.left   = curr.cycleW;\n\t});\n\topts.cssBefore = { left: w, top: 0 };\n\topts.animIn = { left: 0 };\n\topts.animOut  = { left: w };\n};\n// blindY\n$.fn.cycle.transitions.blindY = function($cont, $slides, opts) {\n\tvar h = $cont.css('overflow','hidden').height();\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\topts.animIn.height = next.cycleH;\n\t\topts.animOut.top   = curr.cycleH;\n\t});\n\topts.cssBefore = { top: h, left: 0 };\n\topts.animIn = { top: 0 };\n\topts.animOut  = { top: h };\n};\n// blindZ\n$.fn.cycle.transitions.blindZ = function($cont, $slides, opts) {\n\tvar h = $cont.css('overflow','hidden').height();\n\tvar w = $cont.width();\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\topts.animIn.height = next.cycleH;\n\t\topts.animOut.top   = curr.cycleH;\n\t});\n\topts.cssBefore = { top: h, left: w };\n\topts.animIn = { top: 0, left: 0 };\n\topts.animOut  = { top: h, left: w };\n};\n\n// growX - grow horizontally from centered 0 width\n$.fn.cycle.transitions.growX = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,true);\n\t\topts.cssBefore.left = this.cycleW/2;\n\t\topts.animIn = { left: 0, width: this.cycleW };\n\t\topts.animOut = { left: 0 };\n\t});\n\topts.cssBefore = { width: 0, top: 0 };\n};\n// growY - grow vertically from centered 0 height\n$.fn.cycle.transitions.growY = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,false);\n\t\topts.cssBefore.top = this.cycleH/2;\n\t\topts.animIn = { top: 0, height: this.cycleH };\n\t\topts.animOut = { top: 0 };\n\t});\n\topts.cssBefore = { height: 0, left: 0 };\n};\n\n// curtainX - squeeze in both edges horizontally\n$.fn.cycle.transitions.curtainX = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,false,true,true);\n\t\topts.cssBefore.left = next.cycleW/2;\n\t\topts.animIn = { left: 0, width: this.cycleW };\n\t\topts.animOut = { left: curr.cycleW/2, width: 0 };\n\t});\n\topts.cssBefore = { top: 0, width: 0 };\n};\n// curtainY - squeeze in both edges vertically\n$.fn.cycle.transitions.curtainY = function($cont, $slides, opts) {\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,false,true);\n\t\topts.cssBefore.top = next.cycleH/2;\n\t\topts.animIn = { top: 0, height: next.cycleH };\n\t\topts.animOut = { top: curr.cycleH/2, height: 0 };\n\t});\n\topts.cssBefore = { left: 0, height: 0 };\n};\n\n// cover - curr slide covered by next slide\n$.fn.cycle.transitions.cover = function($cont, $slides, opts) {\n\tvar d = opts.direction || 'left';\n\tvar w = $cont.css('overflow','hidden').width();\n\tvar h = $cont.height();\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts);\n\t\tif (d == 'right')\n\t\t\topts.cssBefore.left = -w;\n\t\telse if (d == 'up')\n\t\t\topts.cssBefore.top = h;\n\t\telse if (d == 'down')\n\t\t\topts.cssBefore.top = -h;\n\t\telse\n\t\t\topts.cssBefore.left = w;\n\t});\n\topts.animIn = { left: 0, top: 0};\n\topts.animOut = { opacity: 1 };\n\topts.cssBefore = { top: 0, left: 0 };\n};\n\n// uncover - curr slide moves off next slide\n$.fn.cycle.transitions.uncover = function($cont, $slides, opts) {\n\tvar d = opts.direction || 'left';\n\tvar w = $cont.css('overflow','hidden').width();\n\tvar h = $cont.height();\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,true,true);\n\t\tif (d == 'right')\n\t\t\topts.animOut.left = w;\n\t\telse if (d == 'up')\n\t\t\topts.animOut.top = -h;\n\t\telse if (d == 'down')\n\t\t\topts.animOut.top = h;\n\t\telse\n\t\t\topts.animOut.left = -w;\n\t});\n\topts.animIn = { left: 0, top: 0 };\n\topts.animOut = { opacity: 1 };\n\topts.cssBefore = { top: 0, left: 0 };\n};\n\n// toss - move top slide and fade away\n$.fn.cycle.transitions.toss = function($cont, $slides, opts) {\n\tvar w = $cont.css('overflow','visible').width();\n\tvar h = $cont.height();\n\topts.before.push(function(curr, next, opts) {\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,true,true);\n\t\t// provide default toss settings if animOut not provided\n\t\tif (!opts.animOut.left && !opts.animOut.top)\n\t\t\topts.animOut = { left: w*2, top: -h/2, opacity: 0 };\n\t\telse\n\t\t\topts.animOut.opacity = 0;\n\t});\n\topts.cssBefore = { left: 0, top: 0 };\n\topts.animIn = { left: 0 };\n};\n\n// wipe - clip animation\n$.fn.cycle.transitions.wipe = function($cont, $slides, opts) {\n\tvar w = $cont.css('overflow','hidden').width();\n\tvar h = $cont.height();\n\topts.cssBefore = opts.cssBefore || {};\n\tvar clip;\n\tif (opts.clip) {\n\t\tif (/l2r/.test(opts.clip))\n\t\t\tclip = 'rect(0px 0px '+h+'px 0px)';\n\t\telse if (/r2l/.test(opts.clip))\n\t\t\tclip = 'rect(0px '+w+'px '+h+'px '+w+'px)';\n\t\telse if (/t2b/.test(opts.clip))\n\t\t\tclip = 'rect(0px '+w+'px 0px 0px)';\n\t\telse if (/b2t/.test(opts.clip))\n\t\t\tclip = 'rect('+h+'px '+w+'px '+h+'px 0px)';\n\t\telse if (/zoom/.test(opts.clip)) {\n\t\t\tvar top = parseInt(h/2);\n\t\t\tvar left = parseInt(w/2);\n\t\t\tclip = 'rect('+top+'px '+left+'px '+top+'px '+left+'px)';\n\t\t}\n\t}\n\n\topts.cssBefore.clip = opts.cssBefore.clip || clip || 'rect(0px 0px 0px 0px)';\n\n\tvar d = opts.cssBefore.clip.match(/(\\d+)/g);\n\tvar t = parseInt(d[0]), r = parseInt(d[1]), b = parseInt(d[2]), l = parseInt(d[3]);\n\n\topts.before.push(function(curr, next, opts) {\n\t\tif (curr == next) return;\n\t\tvar $curr = $(curr), $next = $(next);\n\t\t$.fn.cycle.commonReset(curr,next,opts,true,true,false);\n\t\topts.cssAfter.display = 'block';\n\n\t\tvar step = 1, count = parseInt((opts.speedIn / 13)) - 1;\n\t\t(function f() {\n\t\t\tvar tt = t ? t - parseInt(step * (t/count)) : 0;\n\t\t\tvar ll = l ? l - parseInt(step * (l/count)) : 0;\n\t\t\tvar bb = b < h ? b + parseInt(step * ((h-b)/count || 1)) : h;\n\t\t\tvar rr = r < w ? r + parseInt(step * ((w-r)/count || 1)) : w;\n\t\t\t$next.css({ clip: 'rect('+tt+'px '+rr+'px '+bb+'px '+ll+'px)' });\n\t\t\t(step++ <= count) ? setTimeout(f, 13) : $curr.css('display', 'none');\n\t\t})();\n\t});\n\topts.cssBefore = { display: 'block', opacity: 1, top: 0, left: 0 };\n\topts.animIn\t   = { left: 0 };\n\topts.animOut   = { left: 0 };\n};\n\n})(jQuery);\n"
  },
  {
    "path": "server.js",
    "content": "var ssdp = require(\"peer-ssdp\"),\r\n\tpeer = ssdp.createPeer(),\r\n\tuuid = require('node-uuid'),\r\n\tmyUuid = uuid.v4(),\r\n\tfs = require('fs'),\r\n\texpress = require('express'),\r\n\thttp = require('http'),\r\n\tapp = express(),\r\n\tquerystring = require('querystring'),\r\n\trequest = require('superagent')\r\n\tlogger = require('morgan')\r\n\tbodyParser = require('body-parser')\r\n\tmethodOverride = require('method-override');\r\nvar argv = require('optimist')\r\n    .usage('Usage: $0 --name [name]')\r\n    .demand(['name'])\r\n    .argv;\r\nvar name = argv.name;\r\n\r\napp.set('port', 8008);\r\n\r\napp.use(function(req, res, next) {\r\n\tvar data = '';\r\n\treq.setEncoding('utf8');\r\n\treq.on('data', function(chunk) {\r\n\t\tdata += chunk;\r\n\t});\r\n\treq.on('end', function() {\r\n\t\treq.rawBody = data;\r\n\t\tnext();\r\n\t});\r\n});\r\napp.disable('x-powered-by');\r\napp.use(express.static(__dirname + '/public'));\r\napp.use(logger());\r\napp.use(bodyParser());\r\napp.use(methodOverride());\r\napp.use(function (req, res, next) {\r\n\tres.removeHeader(\"Connection\");\r\n\tnext();\r\n});\r\n\r\napp.disable('x-powered-by');\r\n\r\nvar server = http.createServer(app);\r\n\r\nserver.listen(app.get('port'), function(){\r\n  console.log('Express server listening on port ' + app.get('port'));\r\n});\r\n\r\nvar WebSocketServer = require('websocket').server;\r\nvar WebSocketRouter = require('websocket').router;\r\n\r\nvar wsServer = new WebSocketServer({\r\n    httpServer: server,\r\n    // You should not use autoAcceptConnections for production\r\n    // applications, as it defeats all standard cross-origin protection\r\n    // facilities built into the protocol and the browser.  You should\r\n    // *always* verify the connection's origin and decide whether or not\r\n    // to accept it.\r\n    autoAcceptConnections: false\r\n});\r\n\r\nvar wssRouter = new WebSocketRouter();\r\nwssRouter.attachServer(wsServer);\r\n\r\nwsServer.on('request', function(request) {\r\n});\r\n\r\nwssRouter.mount('/stage','', function(request) {\r\n\tglobal.stageConnection = request.accept(request.origin);\r\n\r\n\tglobal.stageConnection.send(JSON.stringify({\r\n\t\tcmd: \"idle\"\r\n\t}));\r\n});\r\n\r\nwssRouter.mount('/system/control','', function(request) {\r\n\tvar connection = request.accept(request.origin);\r\n\tconsole.log(\"system/control\");\r\n});\r\n\r\nwssRouter.mount('/connection','', function(request) {\r\n\tvar connection = request.accept(request.origin);\r\n\tvar name;\r\n\tconnection.on('message', function(message) {\r\n\t\tvar cmd = JSON.parse(message.utf8Data);\r\n\t\tif(cmd.type == \"REGISTER\") {\r\n\t\t\tname = cmd.name;\r\n\t\t\tconnection.send(JSON.stringify({\r\n\t\t\t\ttype: \"CHANNELREQUEST\",\r\n\t\t\t\t\"senderId\": 1,\r\n\t\t\t\t\"requestId\": 1\r\n\t\t\t}));\r\n\r\n\t\t\twssRouter.mount(\"/receiver/\"+cmd.name, '', function(request) {\r\n\t\t\t\tvar receiverConnection = request.accept(request.origin);\r\n\t\t\t\tvar appName = request.resourceURL.pathname.replace('/receiver/','').replace('Dev','');\r\n\t\t\t\tApps.registered[appName].registerReceiver(receiverConnection);\r\n\t\t\t});\r\n\r\n\t\t} else if(cmd.type == \"CHANNELRESPONSE\") {\r\n\t\t\tconnection.send(JSON.stringify({\r\n\t\t\t\ttype: \"NEWCHANNEL\",\r\n\t\t\t\t\"senderId\": 1,\r\n\t\t\t\t\"requestId\": 1,\r\n\t\t\t\t\"URL\": \"ws://localhost:8008/receiver/\"+name\r\n\t\t\t}));\r\n\t\t}\r\n\t});\r\n});\r\n\r\nvar regex = new RegExp('^/session/.*$');\r\nwssRouter.mount(regex, '', function(request) {\r\n\tvar sessionConn = request.accept(request.origin);\r\n\tconsole.log(\"Session up\");\r\n\r\n\tvar appName = request.resourceURL.pathname.replace('/session/','');\r\n\tvar sessionId = request.resourceURL.search.replace('?','');\r\n\r\n\tvar targetApp = Apps.registered[appName];\r\n\r\n\tif(targetApp) {\r\n\t\ttargetApp.registerSession(sessionConn);\r\n\t}\r\n});\r\n\r\nfunction getIPAddress() {\r\n\tvar n = require('os').networkInterfaces();\r\n\tvar ip = []\r\n\tfor (var k in n) {\r\n\t\tvar inter = n[k];\r\n\t\tfor (var j in inter) {\r\n\t\t\tif (inter[j].family === 'IPv4' && !inter[j].internal) {\r\n\t\t\t\treturn inter[j].address\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvar addr = getIPAddress();\r\nconsole.log(addr);\r\nsetupApps(addr);\r\nsetupRoutes(addr);\r\nsetupSSDP(addr);\r\n\r\n\r\nvar Apps;\r\nfunction setupApps(addr) {\r\n\tApps = require('./apps/apps.js');\r\n\tApps.init(fs, app);\r\n\tApps.registerApp(app, addr, \"ChromeCast\", \"https://www.gstatic.com/cv/receiver.html?$query\", \"\");\r\n\tApps.registerApp(app, addr, \"YouTube\", \"https://www.youtube.com/tv?$query\", \"\");\r\n\tApps.registerApp(app, addr, \"PlayMovies\", \"https://play.google.com/video/avi/eureka?$query\", \"\");\r\n\tApps.registerApp(app, addr, \"GoogleMusic\", \"https://jmt17.google.com/sjdev/cast/player\", \"\");\r\n\tApps.registerApp(app, addr, \"GoogleCastSampleApp\", \"http://anzymrcvr.appspot.com/receiver/anzymrcvr.html\", \"\");\r\n\tApps.registerApp(app, addr, \"GoogleCastPlayer\", \"https://www.gstatic.com/eureka/html/gcp.html\", \"\");\r\n\tApps.registerApp(app, addr, \"Fling\", \"$query\", \"\");\r\n\tApps.registerApp(app, addr, \"TicTacToe\", \"http://www.gstatic.com/eureka/sample/tictactoe/tictactoe.html\", \"\");\r\n}\r\n\r\nfunction setupRoutes(addr) {\r\n\tapp.get(\"/ssdp/device-desc.xml\", function(req, res) {\r\n\t\tfs.readFile('./device-desc.xml', 'utf8', function (err,data) {\r\n\t\t\tdata = data.replace(\"#uuid#\", myUuid).replace(\"#base#\",\"http://\"+req.headers.host).replace(\"#name#\", name);\r\n\t\t\tres.type('xml');\r\n\t\t\tres.setHeader(\"Access-Control-Allow-Method\", \"GET, POST, DELETE, OPTIONS\");\r\n\t\t\tres.setHeader(\"Access-Control-Expose-Headers\", \"Location\");\r\n\t\t\tres.setHeader(\"Application-Url\", \"http://\"+req.headers.host+\"/apps\");\r\n\t\t\tres.send(data);\r\n\t\t});\r\n\t});\r\n\r\n\tapp.post('/connection/:app', function(req, res) {\r\n\t\tconsole.log(\"Connecting App \"+ req.params.app);\r\n\r\n\t\tres.setHeader(\"Access-Control-Allow-Method\", \"POST, OPTIONS\");\r\n\t\tres.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\r\n\r\n\t\tres.type(\"json\");\r\n\t\tres.send(JSON.stringify({\r\n\t\t\tURL: \"ws://\"+addr+\":8008/session/\"+req.params.app+\"?1\",\r\n\t\t\tpingInterval: 3\r\n\t\t}))\r\n\t});\r\n\r\n\tapp.get('/apps', function(req, res){\r\n\t\tfor (var key in Apps.registered) {\r\n\t\t\tif(Apps.registered[key].config.state == \"running\") {\r\n\t\t\t\tconsole.log(\"Redirecting to\"+ key);\r\n\t\t\t\tres.redirect('/apps/'+key);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tres.setHeader(\"Access-Control-Allow-Method\", \"GET, POST, DELETE, OPTIONS\");\r\n\t\tres.setHeader(\"Access-Control-Expose-Headers\", \"Location\");\r\n\t\tres.setHeader(\"Content-Length\", \"0\");\r\n\t\tres.setHeader(\"Content-Type\", \"text/html; charset=UTF-8\");\r\n\t\tres.send(204, \"\");\r\n\r\n\t});\r\n}\r\n\r\nfunction setupSSDP(addr) {\r\n\tpeer.on(\"ready\",function(){\r\n\t});\r\n\r\n\tpeer.on(\"notify\",function(headers, address){\r\n\t});\r\n\r\n\tpeer.on(\"search\",function(headers, address){\r\n\t\tif(headers.ST.indexOf(\"dial-multiscreen-org:service:dial:1\") != -1) {\r\n\t\t\tpeer.reply({\r\n\t\t\t\tLOCATION: \"http://\"+addr+\":8008/ssdp/device-desc.xml\",\r\n\t\t\t\tST: \"urn:dial-multiscreen-org:service:dial:1\",\r\n\t\t\t\t\"CONFIGID.UPNP.ORG\": 7337,\r\n\t\t\t\t\"BOOTID.UPNP.ORG\": 7337,\r\n\t\t\t\tUSN: \"uuid:\"+myUuid\r\n\t\t\t}, address);\r\n\t\t}\r\n\t});\r\n\r\n\tpeer.start();\r\n}\r\n//\r\n"
  }
]