Browse files

add game

  • Loading branch information...
1 parent 4fa7f07 commit 6f3c88f0f210aa1ff9570259528563c18142b61d py committed Nov 30, 2012
Showing with 7,780 additions and 54 deletions.
  1. +16 −54 README.md
  2. +21 −0 game-server/app.js
  3. +62 −0 game-server/app/servers/connector/handler/entryHandler.js
  4. +235 −0 game-server/app/servers/draw/handler/drawHandler.js
  5. +64 −0 game-server/app/servers/draw/remote/drawRemote.js
  6. +41 −0 game-server/app/servers/gate/handler/gateHandler.js
  7. +22 −0 game-server/app/servers/status/handler/statusHandler.js
  8. +37 −0 game-server/app/servers/status/remote/statusRemote.js
  9. +6 −0 game-server/app/util/dispatcher.js
  10. +17 −0 game-server/app/util/routeUtil.js
  11. +64 −0 game-server/config/log4js.json
  12. +14 −0 game-server/config/master.json
  13. +39 −0 game-server/config/servers.json
  14. +1 −0 game-server/logs/tmp
  15. +10 −0 game-server/package.json
  16. +5 −0 npm-install.sh
  17. +5 −0 start.sh
  18. +27 −0 web-server/app.js
  19. +8 −0 web-server/package.json
  20. +322 −0 web-server/public/css/style.css
  21. +47 −0 web-server/public/css/style.styl
  22. BIN web-server/public/img/glyphicons-halflings-white.png
  23. BIN web-server/public/img/glyphicons-halflings.png
  24. +150 −0 web-server/public/index.html
  25. +784 −0 web-server/public/js/client.js
  26. +2 −0 web-server/public/js/lib/jquery-1.8.0.min.js
  27. +456 −0 web-server/public/js/lib/pomeloclient.js
  28. +3,788 −0 web-server/public/js/lib/socket.io.js
  29. +772 −0 web-server/public/vendor/JS3.js
  30. +632 −0 web-server/public/vendor/bootstrap.min.css
  31. +94 −0 web-server/public/vendor/dat.gui.min.js
  32. +1 −0 web-server/public/views/404.jade
  33. +8 −0 web-server/public/views/footer.jade
  34. +3 −0 web-server/public/views/header.jade
  35. +10 −0 web-server/public/views/index.jade
  36. +17 −0 web-server/public/views/layout.jade
View
70 README.md
@@ -1,64 +1,26 @@
-Here at GitHub, we're no strangers to hosting or sponsoring hackathons. With the growing number of games and game development resources on GitHub, we thought it was about time to throw our very own game jam!
+## Draw & Guess
-## The Challenge
+A new style draw & guess game using pomelo framework and html5.
+The game currently runs on nodejs v0.8, and should run fine on the latest stable as well.It requires the following npm libraries:
+- pomelo
+- express
+- crc
-You have the entire month of November to create a **web-based** game *loosely* built around one or more of the following themes:
-
-* forking (or forks)
-* branching (or branches)
-* cloning (or clones)
-* pushing
-* pulling
-
-What do we mean by **loosely** based on these concepts? We literally mean, *loosely* based. Some examples might be a FPS where you throw forks at water balloons, an educational game about DNA cloning, or perhaps a platformer where you push and pull objects.
-
-Your game. Your rules. You can participate as an individual or as a team. You're encouraged to use open source libraries, frameworks, graphics, and sounds.
-
-## Prizes
-
-We have 5 shiny new iPads with Retina displays (64GB wifi models) to give to our winners (or Apple Store Credit equivalent). Runners up will receive GitHub swag of their choice ($100 credit for the [GitHub Shop](http://shop.github.com/)). If you have a team submission, we'll give you Apple Store credit equal to the value of the iPad. You can split it with your teammates as appropriate.
-
-All of the winners and runners up will be showcased on our blog.
-
-<img src="http://i.imgur.com/lxZrD.png" style="border:0;">
-
-### Everyone's a winner!
-
-All participants will receive a limited edition [Coderwall](http://www.coderwall.com) badge as shown above. Winners and runners up will also get their own special version of it.
-
-## Judging
-
-We have a number of awesome judges who graciously volunteered to take a look at all the entries!
-
-* [David Czarnecki](http://twitter.com/CzarneckiD), Lead Engineer at Agora Games
-* [Eric Preisz](https://twitter.com/epreisz), CEO of GarageGames
-* [Matt Hackett](https://twitter.com/#!/richtaur), Co-founder of Lost Decade Games
-* [Lee Reilly](http://twitter.com/leereilly), Gamer Dad and Software Developer at GitHub
-* [Romana Ramzan](https://twitter.com/Manak/), Denki's Player Champion. PhD Researcher. Organiser of Scottish Game Jam.
+Both of them can be installed via 'sh npm-install.sh' (it will install a local copy of all the dependencies in the node_modules directory)
## Rules
+The game can be started at least two players in the room, and if this condition is satisfied the game can be started by anyone in the room. After starting, one player will be selected in random to push what he/she want to draw, and others can guess the answer. The drawer also can clone some shapes such as circle, rectangle, triangle to help player to draw pictures.If someone gets the answer, both he and the drawer get one point.
-* To qualify for entry as an **individual** you must fork the [github/game-off-2012](https://github.com/github/game-off-2012) repository to your individual account
-* To qualify for entry as a **team** you must fork the [github/game-off-2012](https://github.com/github/game-off-2012) to a [free organization account](https://github.com/settings/organizations)
-* All entries must be web-based i.e. playable in a browser. HTML5, WebGL, Unity, Torque 3D, Node JS, Flash is all possible - just be sure the source is made available on your fork.
-* You must be over the age of 13
-
-## Instructions
+## Viewing
-* If you don't already have a GitHub account, [sign up now](https://github.com/signup/free) - it's free!
-* Fork the [github/game-off-2012](https://github.com/github/game-off-2012) repository to your individual account (or to a free organization account)
-* Be sure to follow @github on Twitter for updates
-* Make sure your code is pushed to the master branch of before Dec 1st!
-* Make sure you have a README file with a brief description, what open source projects (if any) you used, and a screenshot.
-* Your repo should have a brief description and a URL where the game is playable entered into the fields shown below (this will make our judging process easier):
+ * Visit [demo game github](https://github.com/py8765/game-off-2012.git) to get the source code and install it on your local machine.
-![](https://img.skitch.com/20121010-x2ecpu95fi91us6hbfehg2dgit.png)
+## Configuration
-Winners will be announced before Christmas :santa:
+ * The server setting (server number, host and port, etc.) can be configured in 'game-server/config/servers.json' and 'game-server/config/master.json' files.
+ * Other settings (log4js etc.) also can be configured in 'game-server/config' folder.
-# Comments / Questions / Help
+## Deployment
+Enter pomelo-draw/game-server, and run 'pomelo start' or 'node app.js' in order to start the game server.
+Enter pomelo-draw/web-server, and run 'node app.js' in order to start the web server, and access '3001' port (which can be changed in 'app_express.js') to load game.
-* New to Git, GitHub, and/or version control? Check out our [help documentation](https://help.github.com/) to get started!
-* Questions about Git/GitHub? Please email support@github.com and be sure to include 'GitHub Game Off' in the subject.
-* Questions specific to the GitHub Game Off? Please [create an issue](https://github.com/github/game-off-2012/issues/new). That will be the offical FAQ.
-* The official Twitter hashtag is [#ggo12](https://twitter.com/search/realtime?q=%23ggo12).
View
21 game-server/app.js
@@ -0,0 +1,21 @@
+var pomelo = require('pomelo');
+var routeUtil = require('./app/util/routeUtil');
+
+/**
+ * Init app for client.
+ */
+var app = pomelo.createApp();
+app.set('name', 'pomelo-draw');
+app.defaultConfiguration();
+
+app.configure('production|development', function() {
+ // route configures
+ app.route('draw', routeUtil.draw);
+});
+
+// start app
+app.start();
+
+process.on('uncaughtException', function (err) {
+ console.error(' Caught exception: ' + err.stack);
+});
View
62 game-server/app/servers/connector/handler/entryHandler.js
@@ -0,0 +1,62 @@
+module.exports = function(app) {
+ return new Handler(app);
+};
+
+var Handler = function(app) {
+ this.app = app;
+ };
+
+
+/**
+ * New client entry chat server.
+ *
+ * @param {Object} msg request message
+ * @param {Object} session current session object
+ * @param {Function} next next stemp callback
+ * @return {Void}
+ */
+Handler.prototype.entry = function(msg, session, next) {
+ var username = msg.username;
+ var rid = msg.rid;
+ var self = this;
+ var uid = username + '*' + rid;
+
+ var sessionService = self.app.get('sessionService');
+
+ if( !! sessionService.getByUid(uid)) {
+ next(null, {
+ code: 500,
+ error: true
+ });
+ return;
+ }
+ session.bind(uid);
+ session.set('rid', rid);
+ session.push('rid', function(err) {
+ if(err) {
+ console.error('set rid for session service failed! error is : %j', err.stack);
+ }
+ });
+ session.on('closed', onUserLeave.bind(null, self.app));
+
+ self.app.rpc.status.statusRemote.push(session, rid, uid, null);
+
+ self.app.rpc.draw.drawRemote.add(session, uid, self.app.get('serverId'), rid, true, function(users) {
+ next(null, {
+ code: 200,
+ users: users,
+ username: username,
+ rid: rid,
+ success: true
+ });
+ });
+};
+
+
+var onUserLeave = function(app, session, reason) {
+ if(!session || !session.uid) {
+ return;
+ }
+ app.rpc.status.statusRemote.remove(session, session.uid, null);
+ app.rpc.draw.drawRemote.kick(session, session.uid, app.get('serverId'), session.rid, false, null);
+ };
View
235 game-server/app/servers/draw/handler/drawHandler.js
@@ -0,0 +1,235 @@
+var drawRemote = require('../remote/drawRemote');
+
+module.exports = function(app) {
+ return new Handler(app);
+};
+
+var Handler = function(app) {
+ this.app = app;
+ this.channelService = this.app.get('channelService');
+ this.remote = drawRemote(this.app);
+ };
+
+var draw_data = {};
+var shape_data = {};
+var is_start = {};
+var answer_map = {};
+var user_map = {};
+var score_map = {};
+
+Handler.prototype.queryRank = function(msg, session, next) {
+ var rid = msg.rid;
+ var user = msg.user;
+ var flag = false;
+ if(!score_map[rid]) score_map[rid] = [];
+ for(var i = 0; i < score_map[rid].length; i++) {
+ if(score_map[rid][i][user] !== undefined) {
+ flag = true;
+ break;
+ }
+ }
+ if(!flag) {
+ var map = {};
+ map[user] = 0;
+ score_map[rid].push(map);
+ }
+
+ var arr = score_map[rid];
+
+ for(var i = 1; i < arr.length; i++) {
+ for(var j = 0; j < arr.length - i; j++) {
+ var v1, v2;
+ for(var key in arr[j])
+ v1 = arr[j][key];
+ for(var name in arr[j + 1])
+ v2 = arr[j + 1][name];
+ if(v1 < v2) {
+ var tmp = arr[j];
+ arr[j] = arr[j + 1];
+ arr[j + 1] = tmp;
+ }
+ }
+ }
+
+ next(null, {
+ rank: arr
+ });
+};
+
+Handler.prototype.gameState = function(msg, session, next) {
+ var rid = msg.rid;
+ var flag = msg.state;
+ var channel = this.channelService.getChannel(rid, false);
+ var param = {
+ route: 'onTimeOut',
+ answer: answer_map[rid]
+ };
+ if(flag !== 'query') {
+ is_start[rid] = flag;
+ delete draw_data[rid];
+ delete shape_data[rid];
+ if(flag === 'TimeOut') {
+ channel.pushMessage(param);
+ delete answer_map[rid];
+ delete user_map[rid];
+ }
+ next(null, {
+ state: flag
+ });
+ } else {
+ if( !! is_start[rid]) flag = is_start[rid];
+ next(null, {
+ state: flag,
+ user: user_map[rid]
+ });
+ }
+};
+
+Handler.prototype.drawHistory = function(msg, session, next) {
+ next(null, {
+ line: draw_data[msg.rid],
+ shape: shape_data[msg.rid]
+ });
+};
+
+Handler.prototype.draw = function(msg, session, next) {
+ var data = msg.data;
+ var rid = msg.rid;
+ var param = {
+ route: 'onDrawData',
+ data: data,
+ from: msg.from,
+ target: msg.target,
+ };
+ var channel = this.channelService.getChannel(rid, false);
+ if(!channel) {
+ console.error('room does not exist! room: ' + rid);
+ }
+ if(msg.flag === 0) {
+ if(!draw_data[rid]) draw_data[rid] = [];
+ draw_data[rid].push(data);
+ param['flag'] = 0;
+ } else {
+ if(!shape_data[rid]) shape_data[rid] = [];
+ shape_data[rid].push(data);
+ param['flag'] = 1;
+ }
+ channel.pushMessage(param);
+ next(null, {
+ code: 200
+ });
+};
+
+Handler.prototype.send = function(msg, session, next) {
+ var param = {
+ route: 'onChat',
+ msg: msg.content,
+ from: msg.from,
+ target: msg.target
+ };
+ var rid = msg.rid;
+ var channel = this.channelService.getChannel(msg.rid, false);
+ if(msg.target == 0) {
+ if(msg.content === answer_map[msg.rid] && user_map[msg.rid] !== msg.from) {
+ for(var i = 0; i < score_map[msg.rid].length; i++) {
+ if(score_map[rid][i][msg.from] !== undefined) score_map[rid][i][msg.from] += 1;
+ if(score_map[rid][i][user_map[msg.rid]] !== undefined) score_map[rid][i][user_map[msg.rid]] += 1;
+ }
+ delete answer_map[msg.rid];
+ delete user_map[msg.rid];
+ param['success'] = true;
+ channel.pushMessage(param);
+ next(null, {
+ success: true
+ });
+ } else {
+ param['success'] = false;
+ channel.pushMessage(param);
+ next(null, {
+ success: false
+ });
+ }
+ }
+ else {
+ var tuid = msg.target + '*' + msg.rid;
+ var tsid = channel.getMember(tuid)['sid'];
+ this.channelService.pushMessageByUids(param, [{
+ uid: tuid,
+ sid: tsid
+ }]);
+ }
+ next(null, {
+ route: msg.route
+ });
+};
+
+Handler.prototype.queryDrawer = function(msg, session, next) {
+ var users = this.remote.get(msg.rid, false);
+ var flag = false;
+ for(var i = 0; i < users.length; i++) {
+ if(users[i] === msg.drawer) flag = true;
+ }
+ if(!flag) {
+ var param = {
+ route: 'onDrawer',
+ answer: answer_map[msg.rid]
+ };
+ is_start[msg.rid] = 'end';
+ var channel = this.channelService.getChannel(msg.rid, false);
+ channel.pushMessage(param);
+ }
+};
+
+
+Handler.prototype.getUsers = function(msg, session, next) {
+ var users = this.remote.get(msg.rid, false);
+ next(null, {
+ users: users
+ });
+};
+
+Handler.prototype.clearCanvas = function(msg, session, next) {
+ var param = {
+ route: 'onClear',
+ from: msg.user
+ };
+ delete draw_data[msg.rid];
+ delete shape_data[msg.rid];
+ var channel = this.channelService.getChannel(msg.rid, false);
+ channel.pushMessage(param);
+};
+
+Handler.prototype.pickDrawer = function(msg, session, next) {
+ var num = Math.floor(Math.random() * 10);
+ var users = this.remote.get(msg.rid, false);
+ var channel = this.channelService.getChannel(msg.rid, false);
+ var users_param = {
+ route: 'onPickDrawer',
+ error: 'true'
+ };
+ if(users.length < 2) {
+ channel.pushMessage(users_param);
+ return;
+ }
+ var user_num = Math.floor(Math.random() * users.length);
+ user_map[msg.rid] = users[user_num];
+ var guess_param = {
+ route: 'onPickDrawer',
+ drawer: users[user_num]
+ };
+ channel.pushMessage(guess_param);
+};
+
+Handler.prototype.pushAnswer = function(msg, session, next) {
+ answer_map[msg.rid] = msg.answer;
+ var channel = this.channelService.getChannel(msg.rid, false);
+ var param = {
+ route: 'onGameStart',
+ answer: msg.answer,
+ drawer: msg.drawer
+ };
+ channel.pushMessage(param);
+ next(null, {
+ answer: msg.answer
+ });
+};
View
64 game-server/app/servers/draw/remote/drawRemote.js
@@ -0,0 +1,64 @@
+module.exports = function(app) {
+ return new DrawRemote(app);
+};
+
+var DrawRemote = function(app) {
+ this.app = app;
+ this.channelService = app.get('channelService');
+};
+
+/**
+ * Add user into chat channel.
+ *
+ * @param {String} uid unique id for user
+ * @param {String} sid server id
+ * @param {String} rid channel id
+ * @param {Object} opts parameters for request
+ * @param {Callback} cb
+ *
+ */
+DrawRemote.prototype.add = function(uid, sid, name, flag, cb) {
+ var channel = this.channelService.getChannel(name, flag);
+ if( !! channel) {
+ channel.add(uid, sid);
+ }
+ cb(this.get(name, flag));
+};
+
+/**
+ * Get user from chat channel.
+ *
+ * @param {Object} opts parameters for request
+ * @return {Array} users uids in channel
+ */
+DrawRemote.prototype.get = function(name, flag) {
+ var users = [];
+ var channel = this.channelService.getChannel(name, flag);
+ if( !! channel) {
+ users = channel.getMembers();
+ }
+ for(var i = 0; i < users.length; i++) {
+ if( !! users[i]) {
+ users[i] = users[i].split('*')[0];
+ }
+ }
+ return users;
+};
+
+/**
+ * Kick user out chat channel.
+ *
+ * @param {String} uid unique id for user
+ * @param {String} sid server id
+ * @param {String} rid channel id
+ * @param {Object} opts parameters for request
+ * @param {Callback} cb
+ *
+ */
+DrawRemote.prototype.kick = function(uid, sid, name) {
+ var channel = this.channelService.getChannel(name, false);
+ // leave channel
+ if( !! channel) {
+ channel.leave(uid, sid);
+ }
+};
View
41 game-server/app/servers/gate/handler/gateHandler.js
@@ -0,0 +1,41 @@
+var dispatcher = require('../../../util/dispatcher');
+
+
+/**
+ * Gate handler that dispatch user to connectors.
+ */
+module.exports = function(app) {
+ return new Handler(app);
+};
+
+var Handler = function(app) {
+ this.app = app;
+ };
+
+Handler.prototype.queryEntry = function(msg, session, next) {
+ var uid = msg.uid;
+ var rid = msg.rid;
+ uid = uid + '*' + rid;
+
+ if(!uid) {
+ next(null, {
+ code: 500
+ });
+ return;
+ }
+
+ var connectors = this.app.getServersByType('connector');
+ if(!connectors || connectors.length === 0) {
+ next(null, {
+ code: 500
+ });
+ return;
+ }
+
+ var res = dispatcher.dispatch(uid, connectors);
+ next(null, {
+ code: 200,
+ host: res.host,
+ port: res.wsPort
+ });
+};
View
22 game-server/app/servers/status/handler/statusHandler.js
@@ -0,0 +1,22 @@
+var statusRemote = require('../remote/statusRemote');
+
+/**
+ * Gate handler that dispatch user to connectors.
+ */
+module.exports = function(app) {
+ return new Handler(app);
+};
+
+var Handler = function(app) {
+ this.app = app;
+};
+
+Handler.prototype.queryRooms = function(msg, session, next) {
+ var remote = statusRemote(this.app);
+ remote.query(function(data) {
+ next(null, {
+ code: 200,
+ rooms: data
+ });
+ });
+};
View
37 game-server/app/servers/status/remote/statusRemote.js
@@ -0,0 +1,37 @@
+module.exports = function(app) {
+ return new StatusRemote(app);
+};
+
+var StatusRemote = function(app) {
+ this.app = app;
+};
+
+var rooms = {};
+
+StatusRemote.prototype.push = function(rid, uid, cb) {
+ if(!rooms[rid]) rooms[rid] = [];
+ rooms[rid].push(uid);
+};
+
+StatusRemote.prototype.query = function(cb) {
+ var room_data = {};
+ for(var rid in rooms) {
+ var count = 0;
+ var users = rooms[rid];
+ for(var i = 0; i < users.length; i++) {
+ if( !! users[i]) count++;
+ }
+ room_data[rid] = count;
+ }
+ cb(room_data);
+};
+
+StatusRemote.prototype.remove = function(uid, cb) {
+ var rid = uid.split('*')[1];
+ var users = rooms[rid];
+ if(!users) return;
+ for(var i = 0; i < users.length; i++) {
+ if(uid === users[i]) delete users[i];
+ }
+ rooms[rid] = users;
+};
View
6 game-server/app/util/dispatcher.js
@@ -0,0 +1,6 @@
+var crc = require('crc');
+
+module.exports.dispatch = function(uid, connectors) {
+ var index = Math.abs(crc.crc32(uid)) % connectors.length;
+ return connectors[index];
+};
View
17 game-server/app/util/routeUtil.js
@@ -0,0 +1,17 @@
+var exp = module.exports;
+var dispatcher = require('./dispatcher');
+var exp = module.exports;
+var dispatcher = require('./dispatcher');
+
+exp.draw = function(session, msg, app, cb) {
+ var drawServers = app.getServersByType('draw');
+
+ if(!drawServers || drawServers.length === 0) {
+ cb(new Error('can not find draw servers.'));
+ return;
+ }
+
+ var res = dispatcher.dispatch(session.get('rid'), drawServers);
+
+ cb(null, res.id);
+};
View
64 game-server/config/log4js.json
@@ -0,0 +1,64 @@
+{
+ "appenders": [
+ {
+ "type": "file",
+ "filename": "./logs/node-log-${opts:serverId}.log",
+ "fileSize": 1048576,
+ "layout": {
+ "type": "basic"
+ },
+ "backups": 5
+ },
+ {
+ "type": "console"
+ },
+ {
+ "type": "file",
+ "filename": "./logs/con-log-${opts:serverId}.log",
+ "pattern": "connector",
+ "fileSize": 1048576,
+ "layout": {
+ "type": "basic"
+ }
+ ,"backups": 5,
+ "category":"con-log"
+ },
+ {
+ "type": "file",
+ "filename": "./logs/rpc-log-${opts:serverId}.log",
+ "fileSize": 1048576,
+ "layout": {
+ "type": "basic"
+ }
+ ,"backups": 5,
+ "category":"rpc-log"
+ },
+ {
+ "type": "file",
+ "filename": "./logs/forward-log-${opts:serverId}.log",
+ "fileSize": 1048576,
+ "layout": {
+ "type": "basic"
+ }
+ ,"backups": 5,
+ "category":"forward-log"
+ },
+ {
+ "type": "file",
+ "filename": "./logs/crash.log",
+ "fileSize": 1048576,
+ "layout": {
+ "type": "basic"
+ }
+ ,"backups": 5,
+ "category":"crash-log"
+ }
+ ],
+
+ "levels": {
+ "rpc-log" : "ERROR",
+ "forward-log": "ERROR"
+ },
+
+ "replaceConsole": true
+}
View
14 game-server/config/master.json
@@ -0,0 +1,14 @@
+{
+ "development":{
+ "id":"master-server-1",
+ "host":"127.0.0.1",
+ "port":3005
+ },
+
+ "production":{
+ "id":"master-server-1",
+ "host":"127.0.0.1",
+ "port":3005
+ }
+
+}
View
39 game-server/config/servers.json
@@ -0,0 +1,39 @@
+{
+ "development":{
+ "gate": [
+ { "id": "gate-server-1", "host": "127.0.0.1", "wsPort": 3014 }
+ ],
+ "connector":[
+ { "id":"connector-server-1", "host":"127.0.0.1", "port":4050, "wsPort":3050 },
+ { "id":"connector-server-2", "host":"127.0.0.1", "port":4051, "wsPort":3051 },
+ { "id":"connector-server-3", "host":"127.0.0.1", "port":4052, "wsPort":3052 }
+
+ ],
+ "draw":[
+ { "id":"draw-server-1", "host":"127.0.0.1", "port":6050 },
+ { "id":"draw-server-2", "host":"127.0.0.1", "port":6051 },
+ { "id":"draw-server-3", "host":"127.0.0.1", "port":6052 }
+
+ ],
+ "status":[
+ { "id":"status-server-1", "host":"127.0.0.1", "port":7050 }
+ ]
+ },
+ "production":{
+ "gate": [
+ { "id": "gate-server-1", "host": "127.0.0.1", "wsPort": 3014 }
+ ],
+ "connector":[
+ { "id":"connector-server-1", "host":"127.0.0.1", "port":4050, "wsPort":3050 },
+ { "id":"connector-server-2", "host":"127.0.0.1", "port":4051, "wsPort":3051 },
+ { "id":"connector-server-3", "host":"127.0.0.1", "port":4052, "wsPort":3052 }
+
+ ],
+ "draw":[
+ { "id":"draw-server-1", "host":"127.0.0.1", "port":6050 },
+ { "id":"draw-server-2", "host":"127.0.0.1", "port":6051 },
+ { "id":"draw-server-3", "host":"127.0.0.1", "port":6052 }
+
+ ]
+ }
+}
View
1 game-server/logs/tmp
@@ -0,0 +1 @@
+development:false
View
10 game-server/package.json
@@ -0,0 +1,10 @@
+{
+ "name":"pomelo-draw",
+ "version":"0.0.1",
+ "private":false,
+ "dependencies":{
+ "pomelo":"0.2.0",
+ "crc" : ">=0.0.1"
+ }
+}
+
View
5 npm-install.sh
@@ -0,0 +1,5 @@
+cd ./game-server && npm install -d
+echo '============ game-server npm installed ============'
+cd ..
+cd ./web-server && npm install -d
+echo '============ web-server npm installed ============'
View
5 start.sh
@@ -0,0 +1,5 @@
+cd ./game-server && node app.js
+echo '============ game-server started ============'
+cd ..
+cd ./web-server && node app.js
+echo '============ web-server started ============'
View
27 web-server/app.js
@@ -0,0 +1,27 @@
+var express = require('express');
+var app = express.createServer();
+
+app.configure(function(){
+ app.use(express.methodOverride());
+ app.use(express.bodyParser());
+ app.use(app.router);
+ app.set('view engine', 'jade');
+ app.set('views', __dirname + '/public');
+ app.set('view options', {layout: false});
+ app.set('basepath',__dirname + '/public');
+});
+
+app.configure('development', function(){
+ app.use(express.static(__dirname + '/public'));
+ app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
+});
+
+app.configure('production', function(){
+ var oneYear = 31557600000;
+ app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
+ app.use(express.errorHandler());
+});
+
+console.log("Web server has started.\nPlease log on http://127.0.0.1:3001/index.html");
+
+app.listen(3001);
View
8 web-server/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "pomelo-draw",
+ "version": "0.0.1",
+ "private": false,
+ "dependencies": {
+ "express": "2.5.2"
+ }
+}
View
322 web-server/public/css/style.css
@@ -0,0 +1,322 @@
+html,
+body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background-color: #add8e6;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+#wrap {
+ min-height: 100%;
+}
+
+#joinView {
+ width: 100%;
+ font-size: 13pt;
+ overflow: hidden;
+}
+
+#joinTitle {
+ width: 100%;
+ margin-top: 150px;
+ font-size: 50pt;
+ text-align: center;
+}
+
+#joinView input[type = "text"] {
+ width: 280px;
+ height: 30px;
+ font-size: inherit;
+}
+
+#joinView input[type = "button"] {
+ width: 80px;
+ height: 30px;
+ font-size: inherit;
+}
+
+#joinView table {
+ width: 100%;
+ height: 100%;
+ margin-top: 10%;
+}
+
+#joinView table tr {
+ text-align: center;
+ height: 50%;
+}
+
+#joinView table tr td {
+ padding-top: 25px;
+}
+
+#createView {
+ width: 100%;
+ overflow: hidden;
+}
+
+#createTitle {
+ width: 100%;
+ margin-top: 150px;
+ font-size: 50pt;
+ text-align: center;
+}
+
+#createView input {
+ height: 30px;
+ font-size: 13pt;
+ margin-left: 15px;
+}
+
+#createView table {
+ width: 100%;
+ height: 100%;
+ margin-top: 10%;
+}
+
+#createView table tr {
+ text-align: center;
+ height: 50%;
+}
+
+#createView table tr td {
+ padding-top: 25px;
+}
+
+#createView select {
+ height: 36px;
+ font-size: 13pt;
+ margin-left: 15px;
+}
+
+#mainView {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+#header {
+ height: 40px;
+ background-color: #add8e6;
+ border-bottom: 2px solid #fff;
+}
+
+#clone {
+ margin-top: -32px;
+ margin-left: 450px;
+}
+
+#clone input {
+ margin-left: 5px;
+}
+
+#clone label {
+ margin-left: 2px;
+}
+
+#clone select {
+ height: 30px;
+ margin-left: 10px;
+}
+
+#header input[type ="button"] {
+ width: 110px;
+ height: 30px;
+ font-size: 12pt;
+ margin-left: 10px;
+ margin-top: 4px;
+}
+
+#header span {
+ margin-left: 45px;
+ font-size: 18pt;
+ margin-top: 5px;
+}
+
+#answer {
+ margin-right: 20px;
+ padding-top: 3px;
+ font-family: arial;
+ font-size: 1.5em;
+ color: #000;
+ float: right;
+}
+
+#gui {
+ position: absolute;
+ top: 40px;
+ right: 0;
+}
+
+#rankTitle {
+ position: absolute;
+ top: 172px;
+ right: 0;
+ width: 240px;
+ height: 30px;
+ border: 2px solid #fafad2;
+ background-color: #B088FF;
+}
+
+#rankTitle table {
+ width: 240px;
+}
+
+#rankTitle table tr {
+ text-align: center;
+}
+
+#rankTitle table tr td {
+ font-family: arial;
+ font-weight: bold;
+ font-size: 13pt;
+}
+
+#rank {
+ position: absolute;
+ top: 205px;
+ right: 0;
+ width: 240px;
+ height: 245px;
+ border: 2px solid #fafad2;
+ background-color: #B088FF;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.rankInfo {
+ width: 240px;
+ height: 25px;
+}
+
+.rankName {
+ width: 120px;
+ font-family: arial;
+ text-align: center;
+}
+
+.rankScore {
+ width: 120px;
+ font-family: arial;
+ text-align: center;
+}
+
+#chatHistory {
+ position: fixed;
+ bottom: 90px;
+ background: #fff;
+ display: block;
+ height: 133px;
+ width: 100%;
+ overflow: auto;
+ border: 2px solid #5599FF;
+}
+
+.message {
+ margin: 0.1em 0;
+}
+
+.message td {
+ vertical-align: top;
+}
+
+.nick {
+ font-family: arial;
+ font-weight: bold;
+ padding: 0 1em 0 0.5em;
+}
+
+.date {
+ font-family: arial;
+ font-weight: bold;
+}
+
+#toolbar {
+ position: fixed;
+ width: 100%;
+ bottom: 0;
+ font-family: arial;
+ font-weight: bold;
+ background: #add8e6;
+}
+
+#toolbar ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+#toolbar li {
+ display: block;
+ float: left;
+ padding-left: 10px;
+ margin: 8px 2em 0 0;
+}
+
+#entry {
+ width: 100%;
+ font-size: inherit;
+ padding: 1em;
+ margin: 0;
+ border-width: 0;
+ outline-width: 0;
+ clear: both;
+}
+
+.mesWindow {
+ border: #666 1px solid;
+ background: #fff;
+}
+
+.mesWindowTop {
+ border-bottom: #eee 2px solid;
+ margin-left: 4px;
+ padding: 8px;
+ font-weight: bold;
+ text-align: left;
+ height: 20px;
+ font-size: 12pt;
+}
+
+.mesWindowContent {
+ margin: 4px;
+ height: 120px;
+}
+
+.mesWindowContent table {
+ width: 400px;
+ height: 130px;
+}
+
+.mesWindowContent table tr {
+ text-align: center;
+}
+
+.mesWindowContent table tr td {
+ font-family: arial;
+ font-weight: bold;
+ font-size: 14pt;
+}
+
+.mesWindowContent input[type ="button"] {
+ width: 75px;
+ height: 26px;
+ font-family: arial;
+ font-size: 12pt;
+}
+
+#back {
+ top: 0px;
+ left: 0px;
+ position: absolute;
+ background: #C0C0C0;
+ width: 100%;
+ height: 100%;
+ opacity: 0.6;
+}
View
47 web-server/public/css/style.styl
@@ -0,0 +1,47 @@
+footer_height = 40px
+
+html, body
+ margin: 0
+ padding: 0
+ height: 100%
+ -webkit-user-select: none
+ -khtml-user-select: none
+ -moz-user-select: none
+ -ms-user-select: none
+ -o-user-select: none
+ user-select: none
+
+#wrap
+ min-height: 100%
+
+#header
+ height:40px
+ background-color:#ddd
+
+#connected
+ float:right
+ margin:11px 30px 0 10px
+
+#main
+ overflow: auto
+ padding-bottom: footer_height
+ background-color: white
+
+#gui
+ position: absolute
+ top 40px
+ right 0
+
+#footer
+ position: relative
+ height: footer_height
+ margin-top: - footer_height
+ background-color:#ddd
+
+#credit
+ font-size: .9em
+ text-align: center
+ padding-top: 10px
+
+#nfnd
+ padding:20px
View
BIN web-server/public/img/glyphicons-halflings-white.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN web-server/public/img/glyphicons-halflings.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
150 web-server/public/index.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>
+ Draw & Guess
+ </title>
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
+ </head>
+
+ <body>
+ <div id="wrap">
+ <div id="joinView">
+ <div id="joinTitle">
+ Draw & Guess
+ </div>
+ <table>
+ <tr>
+ <td>
+ <input id="username" type="text" placeholder= "enter your name" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <input id="startBtn" type='button' value="start"/>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="createView">
+ <div id="createTitle">
+ Just Enjoy it!
+ </div>
+ <table>
+ <tr>
+ <td>
+ <input id="room" type="text" placeholder="create your room" style="width: 280px" />
+ <input id="createBtn" type="button" value="create" style="width:80px"
+ />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <select id="roomSlt" style="width: 284px;margin-right:4px" />
+ <input id="joinBtn" type="button" value="join" style="width:80px" />
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="mainView">
+ <div id="header">
+ <div id="answer">
+ </div>
+ <input id="start" type='button' value="Start Game" />
+ <span id="clock">
+ 00:01:30:00
+ </span>
+ <div id="clone">
+ x:
+ <input id="xTxt" type="text" style="width:50px;height:23px" />
+ y:
+ <input id="yTxt" type="text" style="width:50px;height:23px" />
+ size:
+ <input id="size" type="text" style="width:50px;height:23px" />
+ <select id="shape">
+ <option value="circle">
+ circle
+ </option>
+ <option value="rect">
+ rectangle
+ </option>
+ <option value="tri">
+ triangle
+ </option>
+ </select>
+ <input id="cloneBtn" type="button" value="clone" style="width:80px" />
+ </div>
+ </div>
+ <div id="gui">
+ </div>
+ <div id="rankTitle">
+ <table>
+ <tr>
+ <td>
+ Name
+ </td>
+ <td>
+ Score
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="rank">
+ </div>
+ <canvas id="cnvs" width="1800" height="800">
+ </canvas>
+ <div id="chatHistory">
+ </div>
+ <div id="toolbar">
+ <ul id="status">
+ <li>
+ users:
+ <select name="users" id="usersList" style="width: 100px;height: 25px;font-size: 12pt;margin-bottom: 4px">
+ <option value="0">
+ all
+ </option>
+ </select>
+ </li>
+ <li>
+ name:
+ <span id="name">
+ </span>
+ </li>
+ <li>
+ room:
+ <span id="rid">
+ </span>
+ </li>
+ <li>
+ role:
+ <span id="role">
+ </span>
+ </li>
+ </ul>
+ <input tabindex="1" type="text" id="entry" />
+ </div>
+ </div>
+ </div>
+ </body>
+ <script src="js/lib/jquery-1.8.0.min.js">
+
+ </script>
+ <script src="vendor/dat.gui.min.js">
+
+ </script>
+ <script src="vendor/JS3.js">
+
+ </script>
+ <script src="js/lib/pomeloclient.js">
+
+ </script>
+ <script src="js/lib/socket.io.js">
+
+ </script>
+ <script src="js/client.js">
+
+ </script>
+
+</html>
View
784 web-server/public/js/client.js
@@ -0,0 +1,784 @@
+var pomelo = window.pomelo;
+var gui;
+var stage;
+var username;
+var rid;
+var flag = false;
+var timer = null;
+var queryTimer;
+var roomTimer;
+
+
+function showJoin() {
+ $("#joinView").show();
+ $("#mainView").hide();
+ $("#chatHistory").hide();
+ $("#createView").hide();
+ $("#toolbar").hide();
+};
+
+function showRooms() {
+ $("#createView").show();
+ $("#joinView").hide();
+ $("#mainView").hide();
+ $("#chatHistory").hide();
+ $("#toolbar").hide();
+};
+
+function showMain() {
+ $("#joinView").hide();
+ $("#createView").hide();
+ $("#mainView").show();
+ $("#gui").hide();
+ $("#chatHistory").show();
+ $("#toolbar").show();
+ $("#clock").hide();
+ $("#rooms").hide();
+ $("#clone").hide();
+};
+
+util = {
+ urlRE: /https?:\/\/([-\w\.]+)+(:\d+)?(\/([^\s]*(\?\S+)?)?)?/g,
+ // html sanitizer
+ toStaticHTML: function(inputHtml) {
+ inputHtml = inputHtml.toString();
+ return inputHtml.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+ },
+ //pads n with zeros on the left,
+ //digits is minimum length of output
+ //zeroPad(3, 5); returns "005"
+ //zeroPad(2, 500); returns "500"
+ zeroPad: function(digits, n) {
+ n = n.toString();
+ while(n.length < digits)
+ n = '0' + n;
+ return n;
+ },
+ //it is almost 8 o'clock PM here
+ //timeString(new Date); returns "19:49"
+ timeString: function(date) {
+ var minutes = date.getMinutes().toString();
+ var hours = date.getHours().toString();
+ return this.zeroPad(2, hours) + ":" + this.zeroPad(2, minutes);
+ },
+
+ //does the argument only contain whitespace?
+ isBlank: function(text) {
+ var blank = /^\s*$/;
+ return(text.match(blank) !== null);
+ }
+};
+
+// always view the most recent message when it is added
+
+function scrollDown() {
+ var $chat = $("#chatHistory");
+ $chat.scrollTop($chat[0].scrollHeight);
+};
+
+
+// add message on board
+
+function addMessage(from, target, text, time) {
+ var name = (target == 0 ? 'all' : target);
+ if(text === null) return;
+ if(time == null) {
+ // if the time is null or undefined, use the current time.
+ time = new Date();
+ } else if((time instanceof Date) === false) {
+ // if it's a timestamp, interpret it
+ time = new Date(time);
+ }
+ //every message you see is actually a table with 3 cols:
+ // the time,
+ // the person who caused the event,
+ // and the content
+ var messageElement = $(document.createElement("table"));
+ messageElement.addClass("message");
+ // sanitize
+ text = util.toStaticHTML(text);
+ // replace URLs with links
+ text = text.replace(util.urlRE, '<a target="_blank" href="$&">$&</a>');
+ var content = '<tr>' + ' <td class="date">' + util.timeString(time) + '</td>' + ' <td class="nick">' + util.toStaticHTML(from) + ' says to ' + name + ': ' + '</td>' + ' <td class="date">' + text + '</td>' + '</tr>';
+ messageElement.html(content);
+ //the log is the stream that we view
+ $("#chatHistory").append(messageElement);
+ scrollDown();
+};
+
+// init online users in select.
+
+function initSelectUsers() {
+ for(var i = 0; i < users.length; i++) {
+ var slElement = $(document.createElement("option"));
+ slElement.attr("value", users[i]);
+ slElement.text(users[i]);
+ $("#usersList").append(slElement);
+ }
+};
+
+// update online users in select.
+
+function updateSelectUsers() {
+ var route = "draw.drawHandler.getUsers";
+ var selected = $("#usersList").val();
+ pomelo.request(route, {
+ rid: rid
+ }, function(data) {
+ users = data.users;
+ $("#usersList option").each(
+
+ function() {
+ if($(this).val() != 0) $(this).remove();
+ });
+ initSelectUsers();
+ $("#usersList").attr("value", selected);
+ });
+};
+
+// set user name and room id
+
+function setUserInfo() {
+ $("#name").text(username);
+ $("#rid").text(rid);
+};
+
+// query global rooms
+
+function queryRooms(callback) {
+ pomelo.init({
+ host: window.location.hostname,
+ port: 3014,
+ log: true
+ }, function() {
+ pomelo.request('status.statusHandler.queryRooms', function(data) {
+ callback(data.rooms);
+ });
+ });
+};
+
+// enter name handle
+
+function enterName() {
+ username = $("#username").attr("value");
+ if(!username) {
+ showError("user name cannot be none!");
+ return;
+ }
+ showRooms();
+};
+
+// user join & create room
+
+function login(roomid) {
+ rid = roomid;
+ window.rid = roomid;
+ enterRoom(username, rid);
+};
+
+
+// connect to the server
+
+function enterRoom(username, rid) {
+ queryEntry(username, rid, function(host, port) {
+ if(host === '127.0.0.1') {
+ host = window.location.hostname;
+ }
+ pomelo.init({
+ host: host,
+ port: port,
+ log: true
+ }, function() {
+ pomelo.request('connector.entryHandler.entry', {
+ username: username,
+ rid: rid
+ }, function(data) {
+ if(data.error) {
+ showError('Please change your name to login.');
+ return;
+ }
+ window.clearInterval(roomTimer);
+ users = data.users;
+ afterLogin();
+ queryRoomStatus('query', function(data) {
+ if(data.state === 'start') setStarted(data);
+ });
+ });
+ });
+ });
+};
+
+
+// effect after entering room
+
+function afterLogin() {
+ showMain();
+ initSelectUsers();
+ setUserInfo();
+ updateRank();
+ setInterval(function() {
+ updateSelectUsers();
+ updateRank();
+ }, 5 * 1000);
+};
+
+// update the rank of users
+
+function updateRank() {
+ pomelo.request('draw.drawHandler.queryRank', {
+ rid: rid,
+ user: username
+ }, function(data) {
+ $("#rank").empty();
+ for(var i = 0; i < data.rank.length; i++) {
+ for(var key in data.rank[i]) {
+ var messageElement = $(document.createElement("table"));
+ messageElement.addClass("rankInfo");
+ var content = '<tr>' + '<td class = "rankName">' + key + '</td>' + '<td class = "rankScore">' + data.rank[i][key] + '</td>' + '</tr>';
+ messageElement.html(content);
+ $("#rank").append(messageElement);
+ }
+ }
+ });
+};
+
+
+// check if drawer online
+
+function queryDrawer(drawer) {
+ pomelo.request('draw.drawHandler.queryDrawer', {
+ rid: rid,
+ drawer: drawer
+ }, null);
+};
+
+
+// query room status
+
+function queryRoomStatus(state, callback) {
+ pomelo.request('draw.drawHandler.gameState', {
+ rid: rid,
+ state: state
+ }, function(data) {
+ if( !! callback) callback(data);
+ });
+};
+
+// user join started game
+
+function setStarted(data) {
+ $("#start").attr("disabled", true);
+ $("#answer").text(data.user + " is drawing, please guess!");
+ $("#role").text("guess");
+ $("#gui").hide();
+ showCanvas();
+ if(!queryTimer) {
+ queryTimer = window.setInterval(function() {
+ queryDrawer(data.user);
+ }, 2 * 1000);
+ }
+ // draw data history
+ pomelo.request('draw.drawHandler.drawHistory', {
+ rid: rid
+ }, function(data) {
+ var line = data.line;
+ var shape = data.shape;
+ if(!line && !shape) return;
+ for(var i = 0; i < line.length; i++)
+ stage.drawLine(line[i]);
+ for(var j = 0; j < shape.length; j++)
+ drawShape(shape[j]);
+ });
+};
+
+function createRoomTimer() {
+ // query current existing rooms
+ roomTimer = window.setInterval(function() {
+ queryRooms(function(rooms) {
+ $("#roomSlt").empty();
+ for(var rd in rooms) {
+ if(rooms[rd] > 0) {
+ $("#roomSlt").append("<option value= '" + rd + "'>" + rd + "(" + rooms[rd] + ")</option>");
+ }
+ }
+ });
+ }, 2 * 1000);
+};
+
+
+// entry
+$(document).ready(function() {
+ // entry page
+ showJoin();
+
+ // start game
+ $("#startBtn").click(function() {
+ enterName();
+ });
+
+ createRoomTimer();
+
+ // create room
+ $("#createBtn").click(function() {
+ var roomName = $("#room").attr("value");
+ if(!roomName) {
+ showError("room name cannot be none!");
+ return;
+ }
+ login(roomName);
+ });
+
+ // join rooms
+ $("#joinBtn").click(function() {
+ rid = $("#roomSlt").val();
+ login(rid);
+ });
+
+ // game start
+ $("#start").click(function() {
+ $("#clock").hide();
+ $("#clone").hide();
+ pomelo.request('draw.drawHandler.pickDrawer', {
+ rid: rid
+ }, null);
+ });
+
+});
+
+
+
+// query the gate server to find the connector
+
+function queryEntry(uid, rid, callback) {
+ pomelo.init({
+ host: window.location.hostname,
+ port: 3014,
+ log: true
+ }, function() {
+ pomelo.request('gate.gateHandler.queryEntry', {
+ uid: uid,
+ rid: rid
+ }, function(data) {
+ pomelo.disconnect();
+ if(data.code === 500) {
+ showError('There is no server to login currently');
+ return;
+ }
+ callback(data.host, data.port);
+ });
+ });
+};
+
+
+
+// init draw pannel
+
+function initDrawing() {
+ showCanvas();
+ stage.down = start;
+ stage.enter = start;
+ stage.up = stop;
+ stage.leave = stop;
+ if(!flag) gui = new Gui();
+ var x, y;
+
+ function start(e) {
+ if(stage.mousePressed) {
+ x = e.x;
+ y = e.y;
+ stage.move = onMouseMove;
+ }
+ }
+
+ function stop() {
+ stage.move = null;
+ window.document.body.style.cursor = 'default';
+ }
+
+ function onMouseMove(e) {
+ var data = {
+ x1: x,
+ y1: y,
+ x2: e.x,
+ y2: e.y,
+ color: gui.color,
+ strokeWidth: gui.stroke
+ };
+ stage.drawLine(data);
+ pomelo.request('draw.drawHandler.draw', {
+ data: {
+ x1: x,
+ y1: y,
+ x2: e.x,
+ y2: e.y,
+ color: gui.color,
+ strokeWidth: gui.stroke
+ },
+ flag: 0,
+ rid: window.rid
+ }, function(result) {
+ console.log('draw result code: ' + result.code);
+ });
+
+ x = e.x;
+ y = e.y;
+ window.document.body.style.cursor = 'crosshair';
+ }
+};
+
+// show canvas
+
+function showCanvas() {
+ stage = new JS3('cnvs');
+ stage.interactive = true;
+ stage.drawClean = false;
+ stage.windowTitle = '!!!';
+ stage.radial = ['#ffffff', '#CCCCCC'];
+};
+
+// create draw panel
+
+function Gui() {
+ var _color = '#000000';
+ var _stroke = 5;
+ var _onSave = function() {};
+
+ Object.defineProperty(this, "color", {
+ get: function() {
+ return _color;
+ }
+ });
+ Object.defineProperty(this, "stroke", {
+ get: function() {
+ return _stroke;
+ }
+ });
+
+ var o = {
+ 'Line Color': _color,
+ 'Line Thickness': _stroke,
+ 'Clear Canvas': function() {
+ stage.clear();
+ pomelo.request('draw.drawHandler.clearCanvas', {
+ rid: rid,
+ user: username
+ }, function() {});
+ },
+ 'Save as PNG': function() {
+ stage.save();
+ }
+ };
+ var gui = new dat.GUI({
+ autoPlace: false
+ });
+ var c1 = gui.addColor(o, 'Line Color');
+ c1.onChange(function(val) {
+ _color = val;
+ });
+ var s1 = gui.add(o, 'Line Thickness', 1, 10);
+ s1.onChange(function(val) {
+ _stroke = val;
+ });
+ var c1 = gui.add(o, 'Clear Canvas');
+ var sv = gui.add(o, 'Save as PNG');
+ var div = document.getElementById('gui');
+ div.appendChild(gui.domElement);
+};
+
+
+// deal with chat mode
+$("#entry").keypress(function(e) {
+ var route = "draw.drawHandler.send";
+ var target = $("#usersList").val();
+ if(e.keyCode != 13 /* Return */ ) return;
+ var msg = $("#entry").attr("value").replace("\n", "");
+ if(!util.isBlank(msg)) {
+ pomelo.request(route, {
+ rid: rid,
+ content: msg,
+ from: username,
+ target: target
+ }, function(data) {
+ $("#entry").attr("value", ""); // clear the entry field.
+ if(target != 0 && target != username) {
+ addMessage(username, target, msg);
+ $("#chatHistory").show();
+ }
+ });
+ }
+});
+
+
+function showError(message) {
+ showMessageBox('Error', message);
+};
+
+
+var normalelapse = 100;
+var nextelapse = normalelapse;
+var counter;
+var startTime;
+var start = clock.innerText;
+var finish = "00:00:00:00";
+var init_time = start;
+
+
+function run() {
+ counter = 0;
+ startTime = new Date().valueOf();
+ timer = window.setInterval("onTimer()", nextelapse);
+}
+
+function initTimer() {
+ window.clearTimeout(timer);
+ clock.innerText = init_time;
+ start = init_time;
+}
+
+function stop() {
+ window.clearTimeout(timer);
+}
+
+function onTimer() {
+ if(start == finish) {
+ window.clearInterval(timer);
+ queryRoomStatus('TimeOut', null);
+ timer = null;
+ return;
+ }
+ var hms = new String(start).split(":");
+ var ms = new Number(hms[3]);
+ var s = new Number(hms[2]);
+ var m = new Number(hms[1]);
+ var h = new Number(hms[0]);
+
+ ms -= 10;
+ if(ms < 0) {
+ ms = 90;
+ s -= 1;
+ if(s < 0) {
+ s = 59;
+ m -= 1;
+ }
+ if(m < 0) {
+ m = 59;
+ h -= 1;
+ }
+ }
+ var ms = ms < 10 ? ("0" + ms) : ms;
+ var ss = s < 10 ? ("0" + s) : s;
+ var sm = m < 10 ? ("0" + m) : m;
+ var sh = h < 10 ? ("0" + h) : h;
+ start = sh + ":" + sm + ":" + ss + ":" + ms;
+ clock.innerText = start;
+ window.clearInterval(timer);
+ counter++;
+ var counterSecs = counter * 100;
+ var elapseSecs = new Date().valueOf() - startTime;
+ var diffSecs = counterSecs - elapseSecs;
+ nextelapse = normalelapse + diffSecs;
+ if(nextelapse < 0) nextelapse = 0;
+ timer = window.setInterval("onTimer()", nextelapse);
+};
+
+pomelo.on('onDrawData', function(msg) {
+ if(msg.uid === username) {
+ return;
+ }
+ var data = msg.data;
+ if( !! data) {
+ if(msg.flag === 0) stage.drawLine(data);
+ else drawShape(data);
+ }
+});
+
+pomelo.on('onClear', function(data) {
+ if(data.from !== username) stage.clear();
+});
+
+pomelo.on('onPickDrawer', function(data) {
+ if(data.error) {
+ showError("The number of players in your room is less than 2, please wait.");
+ return;
+ }
+ queryRoomStatus('start', null);
+ $("#answer").text("");
+ $("#start").attr("disabled", true);
+ if(data.drawer === username) {
+ $("#role").text('draw');
+ showMessageBox('Message', 'Please push what you want to draw.');
+ $("#answer").append("<input id = 'answerTxt' type = 'text' style = 'height: 23px'></input><input id = 'answerBtn' type = 'button' value='push' style = 'width:60px'></input>");
+ addAnswerListener(data.drawer);
+ addCloneListener();
+ } else {
+ $("#role").text('guess');
+ $("#answer").text(data.drawer + " is pushing answer, please guess!");
+ queryTimer = window.setInterval(function() {
+ queryDrawer(data.drawer);
+ }, 2 * 1000);
+ }
+});
+
+pomelo.on('disconnect', function(reason) {
+ showJoin();
+});
+
+function numRex(value) {
+ var reg = /^[0-9]*[1-9][0-9]*$/;
+ if(!reg.exec(value)) return false;
+ return true;
+}
+
+function addCloneListener() {
+ $("#cloneBtn").click(function() {
+ var x = $("#xTxt").attr("value");
+ var y = $("#yTxt").attr("value");
+ var size = $("#size").attr("value");
+ if(numRex(x) && numRex(y) && numRex(size)) {
+ var shape = $("#shape").val();
+ var data = {
+ x: x,
+ y: y,
+ size: size,
+ color: gui.color,
+ shape: shape
+ };
+ drawShape(data);
+ pomelo.request('draw.drawHandler.draw', {
+ rid: rid,
+ flag: 1,
+ data: data
+ }, null);
+ } else showError("Your parameters may be wrong, please check.");
+ $("#xTxt").val("");
+ $("#yTxt").val("");
+ $("#size").val("");
+ f
+ });
+};
+
+function drawShape(data) {
+ var param = {
+ x: new Number(data.x),
+ y: new Number(data.y),
+ size: new Number(data.size)
+ };
+ switch(data.shape) {
+ case "circle":
+ var c = new JS3Circle(param);
+ c.color = data.color;
+ stage.addChild(c);
+ break;
+ case "rect":
+ var r = new JS3Rect(param);
+ r.color = data.color;
+ stage.addChild(r);
+ break;
+ case "tri":
+ var t = new JS3Tri(param);
+ t.color = data.color;
+ stage.addChild(t);
+ break;
+ default:
+ break;
+ }
+};
+
+function addAnswerListener(drawer) {
+ $("#answerBtn").click(function() {
+ var answer = $("#answerTxt").attr("value");
+ if(!answer) {
+ showError("please push what you want to draw.");
+ return;
+ }
+ pomelo.request('draw.drawHandler.pushAnswer', {
+ rid: rid,
+ drawer: drawer,
+ answer: answer
+ }, function(data) {
+ $("#answer").text("your answer is " + data.answer);
+ });
+ });
+};
+
+pomelo.on('onGameStart', function(data) {
+ run();
+ if(data.drawer === username) {
+ $("#clock").show();
+ $("#clone").show();
+ $("#answer").text("Your turn, please draw: " + data.answer);
+ initDrawing();
+ flag = true;
+ $("#gui").show();
+ } else {
+ $("#answer").text(data.drawer + " is drawing, please guess!");
+ $("#gui").hide();
+ $("#clock").hide();
+ $("#clone").hide();
+ showCanvas();
+ }
+});
+
+
+pomelo.on('onChat', function(data) {
+ if(data.success) {
+ if(data.from === username) {
+ showMessageBox('Message', 'Congratulations! You got it!');
+ queryRoomStatus('end', null);
+ } else showMessageBox('Message', data.from + " got it!");
+ afterGame();
+ updateRank();
+ }
+ addMessage(data.from, data.target, data.msg);
+ $("#chatHistory").show();
+});
+
+pomelo.on('onTimeOut', function(data) {
+ showMessageBox("Message", "Sorry, no one guesses it. The answer is " + data.answer + ".");
+ afterGame();
+});
+
+pomelo.on('onDrawer', function(data) {
+ window.clearInterval(queryTimer);
+ showMessageBox("Message", "Sorry, the drawer runs away. The answer is " + data.answer + ".");
+ afterGame();
+});
+
+
+function afterGame() {
+ stage.clear();
+ window.clearInterval(timer);
+ $("#answer").text("");
+ $("#gui").hide();
+ $("#clone").hide();
+ $("#clock").hide();
+ $("#start").attr("disabled", false);
+ stage = null;
+ initTimer();
+};
+
+function closeWindow() {
+ if(document.getElementById('back') != null) {
+ document.getElementById('back').parentNode.removeChild(document.getElementById('back'));
+ }
+ if(document.getElementById('mesWindow') != null) {
+ document.getElementById('mesWindow').parentNode.removeChild(document.getElementById('mesWindow'));
+ }
+};
+
+function showMessageBox(title, content) {
+ closeWindow();
+ var bWidth = parseInt(document.documentElement.scrollWidth);
+ var bHeight = parseInt(document.documentElement.scrollHeight);
+ var back = document.createElement("div");
+ back.id = "back";
+ document.body.appendChild(back);
+ var mesW = document.createElement("div");
+ mesW.id = "mesWindow";
+ mesW.className = "mesWindow";
+ var left = (bWidth - 400) / 2;
+ mesW.innerHTML = "<div class='mesWindowTop'><span>" + title + "</span></div><div class='mesWindowContent' id='mesWindowContent'><table><tr><td>" + content + "</td></tr><tr><td><input id = 'closeBtn' type = 'button' value ='close' onClick = 'closeWindow()'/></td></tr></div>";
+ styleStr = "left:" + left + "px;top:240px;position:absolute;width:400px;height:180px";
+ mesW.style.cssText = styleStr;
+ document.body.appendChild(mesW);
+};
View
2 web-server/public/js/lib/jquery-1.8.0.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
+(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d<e;d++)p.event.add(b,c,h[c][d])}g.data&&(g.data=p.extend({},g.data))}function bE(a,b){var c;if(b.nodeType!==1)return;b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?(b.parentNode&&(b.outerHTML=a.outerHTML),p.support.html5Clone&&a.innerHTML&&!p.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):c==="input"&&bv.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text),b.removeAttribute(p.expando)}function bF(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bG(a){bv.test(a.type)&&(a.defaultChecked=a.checked)}function bX(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=bV.length;while(e--){b=bV[e]+c;if(b in a)return b}return d}function bY(a,b){return a=b||a,p.css(a,"display")==="none"||!p.contains(a.ownerDocument,a)}function bZ(a,b){var c,d,e=[],f=0,g=a.length;for(;f<g;f++){c=a[f];if(!c.style)continue;e[f]=p._data(c,"olddisplay"),b?(!e[f]&&c.style.display==="none"&&(c.style.display=""),c.style.display===""&&bY(c)&&(e[f]=p._data(c,"olddisplay",cb(c.nodeName)))):(d=bH(c,"display"),!e[f]&&d!=="none"&&p._data(c,"olddisplay",d))}for(f=0;f<g;f++){c=a[f];if(!c.style)continue;if(!b||c.style.display==="none"||c.style.display==="")c.style.display=b?e[f]||"":"none"}return a}function b$(a,b,c){var d=bO.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function b_(a,b,c,d){var e=c===(d?"border":"content")?4:b==="width"?1:0,f=0;for(;e<4;e+=2)c==="margin"&&(f+=p.css(a,c+bU[e],!0)),d?(c==="content"&&(f-=parseFloat(bH(a,"padding"+bU[e]))||0),c!=="margin"&&(f-=parseFloat(bH(a,"border"+bU[e]+"Width"))||0)):(f+=parseFloat(bH(a,"padding"+bU[e]))||0,c!=="padding"&&(f+=parseFloat(bH(a,"border"+bU[e]+"Width"))||0));return f}function ca(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=!0,f=p.support.boxSizing&&p.css(a,"boxSizing")==="border-box";if(d<=0){d=bH(a,b);if(d<0||d==null)d=a.style[b];if(bP.test(d))return d;e=f&&(p.support.boxSizingReliable||d===a.style[b]),d=parseFloat(d)||0}return d+b_(a,b,c||(f?"border":"content"),e)+"px"}function cb(a){if(bR[a])return bR[a];var b=p("<"+a+">").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write("<!doctype html><html><body>"),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h<i;h++)d=g[h],f=/^\+/.test(d),f&&(d=d.substr(1)||"*"),e=a[d]=a[d]||[],e[f?"unshift":"push"](c)}}function cz(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h,i=a[f],j=0,k=i?i.length:0,l=a===cu;for(;j<k&&(l||!h);j++)h=i[j](c,d,e),typeof h=="string"&&(!l||g[h]?h=b:(c.dataTypes.unshift(h),h=cz(a,c,d,e,h,g)));return(l||!h)&&!g["*"]&&(h=cz(a,c,d,e,"*",g)),h}function cA(a,c){var d,e,f=p.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((f[d]?a:e||(e={}))[d]=c[d]);e&&p.extend(!0,a,e)}function cB(a,c,d){var e,f,g,h,i=a.contents,j=a.dataTypes,k=a.responseFields;for(f in k)f in d&&(c[k[f]]=d[f]);while(j[0]==="*")j.shift(),e===b&&(e=a.mimeType||c.getResponseHeader("content-type"));if(e)for(f in i)if(i[f]&&i[f].test(e)){j.unshift(f);break}if(j[0]in d)g=j[0];else{for(f in d){if(!j[0]||a.converters[f+" "+j[0]]){g=f;break}h||(h=f)}g=g||h}if(g)return g!==j[0]&&j.unshift(g),d[g]}function cC(a,b){var c,d,e,f,g=a.dataTypes.slice(),h=g[0],i={},j=0;a.dataFilter&&(b=a.dataFilter(b,a.dataType));if(g[1])for(c in a.converters)i[c.toLowerCase()]=a.converters[c];for(;e=g[++j];)if(e!=="*"){if(h!=="*"&&h!==e){c=i[h+" "+e]||i["* "+e];if(!c)for(d in i){f=d.split(" ");if(f[1]===e){c=i[h+" "+f[0]]||i["* "+f[0]];if(c){c===!0?c=i[d]:i[d]!==!0&&(e=f[0],g.splice(j--,0,e));break}}}if(c!==!0)if(c&&a["throws"])b=c(b);else try{b=c(b)}catch(k){return{state:"parsererror",error:c?k:"No conversion from "+h+" to "+e}}}h=e}return{state:"success",data:b}}function cK(){try{return new a.XMLHttpRequest}catch(b){}}function cL(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cT(){return setTimeout(function(){cM=b},0),cM=p.now()}function cU(a,b){p.each(b,function(b,c){var d=(cS[b]||[]).concat(cS["*"]),e=0,f=d.length;for(;e<f;e++)if(d[e].call(a,b,c))return})}function cV(a,b,c){var d,e=0,f=0,g=cR.length,h=p.Deferred().always(function(){delete i.elem}),i=function(){var b=cM||cT(),c=Math.max(0,j.startTime+j.duration-b),d=1-(c/j.duration||0),e=0,f=j.tweens.length;for(;e<f;e++)j.tweens[e].run(d);return h.notifyWith(a,[j,d,c]),d<1&&f?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:p.extend({},b),opts:p.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:cM||cT(),duration:c.duration,tweens:[],createTween:function(b,c,d){var e=p.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(e),e},stop:function(b){var c=0,d=b?j.tweens.length:0;for(;c<d;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;cW(k,j.opts.specialEasing);for(;e<g;e++){d=cR[e].call(j,a,k,j.opts);if(d)return d}return cU(j,k),p.isFunction(j.opts.start)&&j.opts.start.call(a,j),p.fx.timer(p.extend(i,{anim:j,queue:j.opts.queue,elem:a})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}function cW(a,b){var c,d,e,f,g;for(c in a){d=p.camelCase(c),e=b[d],f=a[c],p.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=p.cssHooks[d];if(g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}}function cX(a,b,c){var d,e,f,g,h,i,j,k,l=this,m=a.style,n={},o=[],q=a.nodeType&&bY(a);c.queue||(j=p._queueHooks(a,"fx"),j.unqueued==null&&(j.unqueued=0,k=j.empty.fire,j.empty.fire=function(){j.unqueued||k()}),j.unqueued++,l.always(function(){l.always(function(){j.unqueued--,p.queue(a,"fx").length||j.empty.fire()})})),a.nodeType===1&&("height"in b||"width"in b)&&(c.overflow=[m.overflow,m.overflowX,m.overflowY],p.css(a,"display")==="inline"&&p.css(a,"float")==="none"&&(!p.support.inlineBlockNeedsLayout||cb(a.nodeName)==="inline"?m.display="inline-block":m.zoom=1)),c.overflow&&(m.overflow="hidden",p.support.shrinkWrapBlocks||l.done(function(){m.overflow=c.overflow[0],m.overflowX=c.overflow[1],m.overflowY=c.overflow[2]}));for(d in b){f=b[d];if(cO.exec(f)){delete b[d];if(f===(q?"hide":"show"))continue;o.push(d)}}g=o.length;if(g){h=p._data(a,"fxshow")||p._data(a,"fxshow",{}),q?p(a).show():l.done(function(){p(a).hide()}),l.done(function(){var b;p.removeData(a,"fxshow",!0);for(b in n)p.style(a,b,n[b])});for(d=0;d<g;d++)e=o[d],i=l.createTween(e,q?h[e]:0),n[e]=h[e]||p.style(a,e),e in h||(h[e]=i.start,q&&(i.end=i.start,i.start=e==="width"||e==="height"?1:0))}}function cY(a,b,c,d,e){return new cY.prototype.init(a,b,c,d,e)}function cZ(a,b){var c,d={height:a},e=0;for(;e<4;e+=2-b)c=bU[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function c_(a){return p.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}var c,d,e=a.document,f=a.location,g=a.navigator,h=a.jQuery,i=a.$,j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf,m=Object.prototype.toString,n=Object.prototype.hasOwnProperty,o=String.prototype.trim,p=function(a,b){return new p.fn.init(a,b,c)},q=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,r=/\S/,s=/\s+/,t=r.test(" ")?/^[\s\xA0]+|[\s\xA0]+$/g:/^\s+|\s+$/g,u=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i<j;i++)if((a=arguments[i])!=null)for(c in a){d=h[c],e=a[c];if(h===e)continue;k&&e&&(p.isPlainObject(e)||(f=p.isArray(e)))?(f?(f=!1,g=d&&p.isArray(d)?d:[]):g=d&&p.isPlainObject(d)?d:{},h[c]=p.extend(k,g,e)):e!==b&&(h[c]=e)}return h},p.extend({noConflict:function(b){return a.$===p&&(a.$=i),b&&a.jQuery===p&&(a.jQuery=h),p},isReady:!1,readyWait:1,holdReady:function(a){a?p.readyWait++:p.ready(!0)},ready:function(a){if(a===!0?--p.readyWait:p.isReady)return;if(!e.body)return setTimeout(p.ready,1);p.isReady=!0;if(a!==!0&&--p.readyWait>0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f<g;)if(c.apply(a[f++],d)===!1)break}else if(h){for(e in a)if(c.call(a[e],e,a[e])===!1)break}else for(;f<g;)if(c.call(a[f],f,a[f++])===!1)break;return a},trim:o?function(a){return a==null?"":o.call(a)}:function(a){return a==null?"":a.toString().replace(t,"")},makeArray:function(a,b){var c,d=b||[];return a!=null&&(c=p.type(a),a.length==null||c==="string"||c==="function"||c==="regexp"||p.isWindow(a)?j.call(d,a):p.merge(d,a)),d},inArray:function(a,b,c){var d;if(b){if(l)return l.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=c.length,e=a.length,f=0;if(typeof d=="number")for(;f<d;f++)a[e++]=c[f];else while(c[f]!==b)a[e++]=c[f++];return a.length=e,a},grep:function(a,b,c){var d,e=[],f=0,g=a.length;c=!!c;for(;f<g;f++)d=!!b(a[f],f),c!==d&&e.push(a[f]);return e},map:function(a,c,d){var e,f,g=[],h=0,i=a.length,j=a instanceof p||i!==b&&typeof i=="number"&&(i>0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h<i;h++)e=c(a[h],h,d),e!=null&&(g[g.length]=e);else for(f in a)e=c(a[f],f,d),e!=null&&(g[g.length]=e);return g.concat.apply([],g)},guid:1,proxy:function(a,c){var d,e,f;return typeof c=="string"&&(d=a[c],c=a,a=d),p.isFunction(a)?(e=k.call(arguments,2),f=function(){return a.apply(c,e.concat(k.call(arguments)))},f.guid=a.guid=a.guid||f.guid||p.guid++,f):b},access:function(a,c,d,e,f,g,h){var i,j=d==null,k=0,l=a.length;if(d&&typeof d=="object"){for(k in d)p.access(a,c,k,d[k],1,g,e);f=1}else if(e!==b){i=h===b&&p.isFunction(e),j&&(i?(i=c,c=function(a,b,c){return i.call(p(a),c)}):(c.call(a,e),c=null));if(c)for(;k<l;k++)c(a[k],d,i?e.call(a[k],k,c(a[k],d)):e,h);f=1}return f?a:j?c.call(a):l?c(a[0],d):g},now:function(){return(new Date).getTime()}}),p.ready.promise=function(b){if(!d){d=p.Deferred();if(e.readyState==="complete"||e.readyState!=="loading"&&e.addEventListener)setTimeout(p.ready,1);else if(e.addEventListener)e.addEventListener("DOMContentLoaded",D,!1),a.addEventListener("load",p.ready,!1);else{e.attachEvent("onreadystatechange",D),a.attachEvent("onload",p.ready);var c=!1;try{c=a.frameElement==null&&e.documentElement}catch(f){}c&&c.doScroll&&function g(){if(!p.isReady){try{c.doScroll("left")}catch(a){return setTimeout(g,50)}p.ready()}}()}}return d.promise(b)},p.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){E["[object "+b+"]"]=b.toLowerCase()}),c=p(e);var F={};p.Callbacks=function(a){a=typeof a=="string"?F[a]||G(a):p.extend({},a);var c,d,e,f,g,h,i=[],j=!a.once&&[],k=function(b){c=a.memory&&b,d=!0,h=f||0,f=0,g=i.length,e=!0;for(;i&&h<g;h++)if(i[h].apply(b[0],b[1])===!1&&a.stopOnFalse){c=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):c?i=[]:l.disable())},l={add:function(){if(i){var b=i.length;(function d(b){p.each(b,function(b,c){p.isFunction(c)&&(!a.unique||!l.has(c))?i.push(c):c&&c.length&&d(c)})})(arguments),e?g=i.length:c&&(f=b,k(c))}return this},remove:function(){return i&&p.each(arguments,function(a,b){var c;while((c=p.inArray(b,i,c))>-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b<d;b++)c[b]&&p.isFunction(c[b].promise)?c[b].promise().done(g(b,j,c)).fail(f.reject).progress(g(b,i,h)):--e}return e||f.resolveWith(j,c),f.promise()}}),p.support=function(){var b,c,d,f,g,h,i,j,k,l,m,n=e.createElement("div");n.setAttribute("className","t"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,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%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="<div></div>",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e<f;e++)delete d[b[e]];if(!(c?K:p.isEmptyObject)(d))return}}if(!c){delete h[i].data;if(!K(h[i]))return}g?p.cleanData([a],!0):p.support.deleteExpando||h!=h.window?delete h[i]:h[i]=null},_data:function(a,b,c){return p.data(a,b,c,!0)},acceptData:function(a){var b=a.nodeName&&p.noData[a.nodeName.toLowerCase()];return!b||b!==!0&&a.getAttribute("classid")===b}}),p.fn.extend({data:function(a,c){var d,e,f,g,h,i=this[0],j=0,k=null;if(a===b){if(this.length){k=p.data(i);if(i.nodeType===1&&!p._data(i,"parsedAttrs")){f=i.attributes;for(h=f.length;j<h;j++)g=f[j].name,g.indexOf("data-")===0&&(g=p.camelCase(g.substring(5)),J(i,g,k[g]));p._data(i,"parsedAttrs",!0)}}return k}return typeof a=="object"?this.each(function(){p.data(this,a)}):(d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!",p.access(this,function(c){if(c===b)return k=this.triggerHandler("getData"+e,[d[0]]),k===b&&i&&(k=p.data(i,a),k=J(i,a,k)),k===b&&d[1]?this.data(d[0]):k;d[1]=c,this.each(function(){var b=p(this);b.triggerHandler("setData"+e,d),p.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length<d?p.queue(this[0],a):c===b?this:this.each(function(){var b=p.queue(this,a,c);p._queueHooks(this,a),a==="fx"&&b[0]!=="inprogress"&&p.dequeue(this,a)})},dequeue:function(a){return this.each(function(){p.dequeue(this,a)})},delay:function(a,b){return a=p.fx?p.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){var d,e=1,f=p.Deferred(),g=this,h=this.length,i=function(){--e||f.resolveWith(g,[g])};typeof a!="string"&&(c=a,a=b),a=a||"fx";while(h--)(d=p._data(g[h],a+"queueHooks"))&&d.empty&&(e++,d.empty.add(i));return i(),f.promise(c)}});var L,M,N,O=/[\t\r\n]/g,P=/\r/g,Q=/^(?:button|input)$/i,R=/^(?:button|input|object|select|textarea)$/i,S=/^a(?:rea|)$/i,T=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,U=p.support.getSetAttribute;p.fn.extend({attr:function(a,b){return p.access(this,p.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{f=" "+e.className+" ";for(g=0,h=b.length;g<h;g++)~f.indexOf(" "+b[g]+" ")||(f+=b[g]+" ");e.className=p.trim(f)}}}return this},removeClass:function(a){var c,d,e,f,g,h,i;if(p.isFunction(a))return this.each(function(b){p(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(s);for(h=0,i=this.length;h<i;h++){e=this[h];if(e.nodeType===1&&e.className){d=(" "+e.className+" ").replace(O," ");for(f=0,g=c.length;f<g;f++)while(d.indexOf(" "+c[f]+" ")>-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(O," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c<d;c++){e=h[c];if(e.selected&&(p.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!p.nodeName(e.parentNode,"optgroup"))){b=p(e).val();if(i)return b;g.push(b)}}return i&&!g.length&&h.length?p(h[f]).val():g},set:function(a,b){var c=p.makeArray(b);return p(a).find("option").each(function(){this.selected=p.inArray(p(this).val(),c)>=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g<d.length;g++)e=d[g],e&&(c=p.propFix[e]||e,f=T.test(e),f||p.attr(a,e,""),a.removeAttribute(U?e:c),f&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(Q.test(a.nodeName)&&a.parentNode)p.error("type property can't be changed");else if(!p.support.radioValue&&b==="radio"&&p.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}},value:{get:function(a,b){return L&&p.nodeName(a,"button")?L.get(a,b):b in a?a.value:null},set:function(a,b,c){if(L&&p.nodeName(a,"button"))return L.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,f,g,h=a.nodeType;if(!a||h===3||h===8||h===2)return;return g=h!==1||!p.isXMLDoc(a),g&&(c=p.propFix[c]||c,f=p.propHooks[c]),d!==b?f&&"set"in f&&(e=f.set(a,d,c))!==b?e:a[c]=d:f&&"get"in f&&(e=f.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):R.test(a.nodeName)||S.test(a.nodeName)&&a.href?0:b}}}}),M={get:function(a,c){var d,e=p.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;return b===!1?p.removeAttr(a,c):(d=p.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase())),c}},U||(N={name:!0,id:!0,coords:!0},L=p.valHooks.button={get:function(a,c){var d;return d=a.getAttributeNode(c),d&&(N[c]?d.value!=="":d.specified)?d.value:b},set:function(a,b,c){var d=a.getAttributeNode(c);return d||(d=e.createAttribute(c),a.setAttributeNode(d)),d.value=b+""}},p.each(["width","height"],function(a,b){p.attrHooks[b]=p.extend(p.attrHooks[b],{set:function(a,c){if(c==="")return a.setAttribute(b,"auto"),c}})}),p.attrHooks.contenteditable={get:L.get,set:function(a,b,c){b===""&&(b="false"),L.set(a,b,c)}}),p.support.hrefNormalized||p.each(["href","src","width","height"],function(a,c){p.attrHooks[c]=p.extend(p.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),p.support.style||(p.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),p.support.optSelected||(p.propHooks.selected=p.extend(p.propHooks.selected,{get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}})),p.support.enctype||(p.propFix.enctype="encoding"),p.support.checkOn||p.each(["radio","checkbox"],function(){p.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]=p.extend(p.valHooks[this],{set:function(a,b){if(p.isArray(b))return a.checked=p.inArray(p(a).val(),b)>=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j<c.length;j++){k=W.exec(c[j])||[],l=k[1],m=(k[2]||"").split(".").sort(),r=p.event.special[l]||{},l=(f?r.delegateType:r.bindType)||l,r=p.event.special[l]||{},n=p.extend({type:l,origType:k[1],data:e,handler:d,guid:d.guid,selector:f,namespace:m.join(".")},o),q=i[l];if(!q){q=i[l]=[],q.delegateCount=0;if(!r.setup||r.setup.call(a,e,m,h)===!1)a.addEventListener?a.addEventListener(l,h,!1):a.attachEvent&&a.attachEvent("on"+l,h)}r.add&&(r.add.call(a,n),n.handler.guid||(n.handler.guid=d.guid)),f?q.splice(q.delegateCount++,0,n):q.push(n),p.event.global[l]=!0}a=null},global:{},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,q,r=p.hasData(a)&&p._data(a);if(!r||!(m=r.events))return;b=p.trim(_(b||"")).split(" ");for(f=0;f<b.length;f++){g=W.exec(b[f])||[],h=i=g[1],j=g[2];if(!h){for(h in m)p.event.remove(a,h+b[f],c,d,!0);continue}n=p.event.special[h]||{},h=(d?n.delegateType:n.bindType)||h,o=m[h]||[],k=o.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(l=0;l<o.length;l++)q=o[l],(e||i===q.origType)&&(!c||c.guid===q.guid)&&(!j||j.test(q.namespace))&&(!d||d===q.selector||d==="**"&&q.selector)&&(o.splice(l--,1),q.selector&&o.delegateCount--,n.remove&&n.remove.call(a,q));o.length===0&&k!==o.length&&((!n.teardown||n.teardown.call(a,j,r.handle)===!1)&&p.removeEvent(a,h,r.handle),delete m[h])}p.isEmptyObject(m)&&(delete r.handle,p.removeData(a,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,f,g){if(!f||f.nodeType!==3&&f.nodeType!==8){var h,i,j,k,l,m,n,o,q,r,s=c.type||c,t=[];if($.test(s+p.event.triggered))return;s.indexOf("!")>=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j<q.length&&!c.isPropagationStopped();j++)k=q[j][0],c.type=q[j][1],o=(p._data(k,"events")||{})[c.type]&&p._data(k,"handle"),o&&o.apply(k,d),o=m&&k[m],o&&p.acceptData(k)&&o.apply(k,d)===!1&&c.preventDefault();return c.type=s,!g&&!c.isDefaultPrevented()&&(!n._default||n._default.apply(f.ownerDocument,d)===!1)&&(s!=="click"||!p.nodeName(f,"a"))&&p.acceptData(f)&&m&&f[s]&&(s!=="focus"&&s!=="blur"||c.target.offsetWidth!==0)&&!p.isWindow(f)&&(l=f[m],l&&(f[m]=null),p.event.triggered=s,f[s](),p.event.triggered=b,l&&(f[m]=l)),c.result}return},dispatch:function(c){c=p.event.fix(c||a.event);var d,e,f,g,h,i,j,k,l,m,n,o=(p._data(this,"events")||{})[c.type]||[],q=o.delegateCount,r=[].slice.call(arguments),s=!c.exclusive&&!c.namespace,t=p.event.special[c.type]||{},u=[];r[0]=c,c.delegateTarget=this;if(t.preDispatch&&t.preDispatch.call(this,c)===!1)return;if(q&&(!c.button||c.type!=="click")){g=p(this),g.context=this;for(f=c.target;f!=this;f=f.parentNode||this)if(f.disabled!==!0||c.type!=="click"){i={},k=[],g[0]=f;for(d=0;d<q;d++)l=o[d],m=l.selector,i[m]===b&&(i[m]=g.is(m)),i[m]&&k.push(l);k.length&&u.push({elem:f,matches:k})}}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d<u.length&&!c.isPropagationStopped();d++){j=u[d],c.currentTarget=j.elem;for(e=0;e<j.matches.length&&!c.isImmediatePropagationStopped();e++){l=j.matches[e];if(s||!c.namespace&&!l.namespace||c.namespace_re&&c.namespace_re.test(l.namespace))c.data=l.data,c.handleObj=l,h=((p.event.special[l.origType]||{}).handle||l.handler).apply(j.elem,r),h!==b&&(c.result=h,h===!1&&(c.preventDefault(),c.stopPropagation()))}}return t.postDispatch&&t.postDispatch.call(this,c),c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,c){var d,f,g,h=c.button,i=c.fromElement;return a.pageX==null&&c.clientX!=null&&(d=a.target.ownerDocument||e,f=d.documentElement,g=d.body,a.pageX=c.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=c.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?c.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0),a}},fix:function(a){if(a[p.expando])return a;var b,c,d=a,f=p.event.fixHooks[a.type]||{},g=f.props?this.props.concat(f.props):this.props;a=p.Event(d);for(b=g.length;b;)c=g[--b],a[c]=d[c];return a.target||(a.target=d.srcElement||e),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,f.filter?f.filter(a,d):a},special:{ready:{setup:p.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){p.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=p.extend(new p.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?p.event.trigger(e,null,b):p.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},p.event.handle=p.event.dispatch,p.removeEvent=e.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]=="undefined"&&(a[d]=null),a.detachEvent(d,c))},p.Event=function(a,b){if(this instanceof p.Event)a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?bb:ba):this.type=a,b&&p.extend(this,b),this.timeStamp=a&&a.timeStamp||p.now(),this[p.expando]=!0;else return new p.Event(a,b)},p.Event.prototype={preventDefault:function(){this.isDefaultPrevented=bb;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=bb;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(