├── .npmignore ├── README ├── public ├── favicon.ico ├── images │ ├── Pen.cur │ ├── pen.ani │ ├── Thumbs.db │ ├── blank.gif │ ├── select.png │ ├── slider.png │ ├── select2.png │ ├── custom_hex.png │ ├── custom_hsb_b.png │ ├── custom_hsb_h.png │ ├── custom_hsb_s.png │ ├── custom_indic.gif │ ├── custom_rgb_b.png │ ├── custom_rgb_g.png │ ├── custom_rgb_r.png │ ├── custom_submit.png │ ├── colorpicker_hex.png │ ├── colorpicker_hsb_b.png │ ├── colorpicker_hsb_h.png │ ├── colorpicker_hsb_s.png │ ├── colorpicker_indic.gif │ ├── colorpicker_rgb_b.png │ ├── colorpicker_rgb_g.png │ ├── colorpicker_rgb_r.png │ ├── colorpicker_select.gif │ ├── colorpicker_submit.png │ ├── custom_background.png │ ├── colorpicker_overlay.png │ └── colorpicker_background.png ├── stylesheets │ ├── style.css │ └── colorpicker.css └── js │ ├── jquery.mpaint.js │ ├── colorpicker.js │ ├── excanvas.js │ └── bootstrap.js ├── routes ├── index.js └── user.js ├── package.json ├── app.js └── views └── index.ejs /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 基于HTML5 WebSocket和Canvas创建的多人在线画图程序(你画我猜) -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/images/Pen.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/Pen.cur -------------------------------------------------------------------------------- /public/images/pen.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/pen.ani -------------------------------------------------------------------------------- /public/images/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/Thumbs.db -------------------------------------------------------------------------------- /public/images/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/blank.gif -------------------------------------------------------------------------------- /public/images/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/select.png -------------------------------------------------------------------------------- /public/images/slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/slider.png -------------------------------------------------------------------------------- /public/images/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/select2.png -------------------------------------------------------------------------------- /public/images/custom_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_hex.png -------------------------------------------------------------------------------- /public/images/custom_hsb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_hsb_b.png -------------------------------------------------------------------------------- /public/images/custom_hsb_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_hsb_h.png -------------------------------------------------------------------------------- /public/images/custom_hsb_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_hsb_s.png -------------------------------------------------------------------------------- /public/images/custom_indic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_indic.gif -------------------------------------------------------------------------------- /public/images/custom_rgb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_rgb_b.png -------------------------------------------------------------------------------- /public/images/custom_rgb_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_rgb_g.png -------------------------------------------------------------------------------- /public/images/custom_rgb_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_rgb_r.png -------------------------------------------------------------------------------- /public/images/custom_submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_submit.png -------------------------------------------------------------------------------- /public/images/colorpicker_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_hex.png -------------------------------------------------------------------------------- /public/images/colorpicker_hsb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_hsb_b.png -------------------------------------------------------------------------------- /public/images/colorpicker_hsb_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_hsb_h.png -------------------------------------------------------------------------------- /public/images/colorpicker_hsb_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_hsb_s.png -------------------------------------------------------------------------------- /public/images/colorpicker_indic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_indic.gif -------------------------------------------------------------------------------- /public/images/colorpicker_rgb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_rgb_b.png -------------------------------------------------------------------------------- /public/images/colorpicker_rgb_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_rgb_g.png -------------------------------------------------------------------------------- /public/images/colorpicker_rgb_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_rgb_r.png -------------------------------------------------------------------------------- /public/images/colorpicker_select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_select.gif -------------------------------------------------------------------------------- /public/images/colorpicker_submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_submit.png -------------------------------------------------------------------------------- /public/images/custom_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/custom_background.png -------------------------------------------------------------------------------- /public/images/colorpicker_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_overlay.png -------------------------------------------------------------------------------- /public/images/colorpicker_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mz121star/WebsocketPaint/HEAD/public/images/colorpicker_background.png -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * GET home page. 4 | */ 5 | 6 | exports.index = function(req, res){ 7 | res.render('index', { title: '你画我猜' }); 8 | }; -------------------------------------------------------------------------------- /routes/user.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * GET users listing. 4 | */ 5 | 6 | exports.list = function(req, res){ 7 | res.send("respond with a resource"); 8 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "application-name", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app.js" 7 | }, 8 | "dependencies": { 9 | "express": "3.2.6", 10 | "ejs": "*", 11 | "socket.io":"*" 12 | } 13 | } -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | margin: 0 auto; 4 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 5 | } 6 | 7 | a { 8 | color: #00B7FF; 9 | } 10 | 11 | select{ 12 | border: 0px; 13 | overflow: auto; 14 | } 15 | select:focus { 16 | border: 0px; 17 | 18 | } 19 | #container{width: 800px;margin-left: 200px;} 20 | #graph { 21 | border: 1px solid #87ceeb; 22 | cursor: url(../images/Pen.cur), auto; 23 | 24 | } 25 | #graph:hover { 26 | 27 | cursor: url(../images/Pen.cur), auto; 28 | 29 | } 30 | #userlist { 31 | width: 200px; 32 | height: 490px; 33 | border: 1px solid #87ceeb; 34 | float: left; 35 | margin-left: 10px;; 36 | padding: 5px 0; 37 | } 38 | 39 | #users { 40 | width: 200px; 41 | } 42 | 43 | #message { 44 | height: 100px; 45 | overflow-y: auto; 46 | } 47 | 48 | #messagebox { 49 | clear: both; 50 | width: 712px; 51 | border: 1px solid #87ceeb; 52 | } 53 | 54 | #drawCanvas{ 55 | float: left; 56 | width: 500px; 57 | } 58 | 59 | #drawCanvas #colorSelector{ 60 | 61 | background-color: #000; 62 | } 63 | .colorpicker input{ 64 | border: 0px; 65 | border-color: transparent; 66 | width: 50px; 67 | } 68 | .colorpicker input:focus,.colorpicker input:hover{ 69 | border: 0px; 70 | border-color: transparent; 71 | -webkit-box-shadow: 0 72 | -moz-box-shadow: 0 73 | box-shadow: 0 74 | } 75 | #tools{ 76 | float: left; 77 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('express') 6 | , routes = require('./routes') 7 | , user = require('./routes/user') 8 | , http = require('http') 9 | , path = require('path'); 10 | 11 | var app = express(); 12 | 13 | // all environments 14 | app.set('port', process.env.PORT || 3000); 15 | app.set('views', __dirname + '/views'); 16 | app.set('view engine', 'ejs'); 17 | app.use(express.favicon()); 18 | app.use(express.logger('dev')); 19 | app.use(express.bodyParser()); 20 | app.use(express.methodOverride()); 21 | app.use(express.cookieParser('your secret here')); 22 | app.use(express.session()); 23 | app.use(app.router); 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | 26 | // development only 27 | if ('development' == app.get('env')) { 28 | app.use(express.errorHandler()); 29 | } 30 | 31 | app.get('/', routes.index); 32 | app.get('/users', user.list); 33 | 34 | var server = http.createServer(app), 35 | io = require('socket.io').listen(server); 36 | server.listen(app.get('port'), function () { 37 | console.log('Express server listening on port ' + app.get('port')); 38 | }); 39 | 40 | io.sockets.on('connection', function (socket) { 41 | socket.emit('draw', { hello:'world' }); 42 | socket.on('draw', function (data) { 43 | console.log(data); 44 | socket.broadcast.emit('draw', data); 45 | }); 46 | socket.on('set_nickname', function (name) { 47 | socket.set('nickname', name, function () { 48 | 49 | //socket.broadcast.emit('add user',socket.manager.roomClients); 50 | socket.emit('add_user', socket.manager.roomClients); 51 | socket.broadcast.emit('add_user', socket.manager.roomClients); 52 | //console.log(socket); 53 | }); 54 | }); 55 | socket.on('check_nickname', function (name) { 56 | for(var i= 0,l=socket.manager.roomClients.length;i对你说:" + data.msg) 65 | } 66 | else { 67 | socket.broadcast.emit("msg", socket.store.data.nickname + "对大家说:" + data.msg); 68 | } 69 | }); 70 | }); -------------------------------------------------------------------------------- /public/js/jquery.mpaint.js: -------------------------------------------------------------------------------- 1 | (function ($, window) { 2 | var ctx, needRestore = true, readydraw = false; 3 | $.fn.MPaint = function (opts) { 4 | var opts = $.extend({}, $.fn.MPaint.defaults, opts), begin = false; 5 | var _moveto = function (e) { 6 | e.preventDefault(); 7 | var x = e.offsetX || e.touches[0].clientX + e.layerX, y = e.offsetY || e.touches[0].clientY + e.layerY; 8 | begin = true; 9 | ctx.beginPath(); 10 | ctx.moveTo(x, y); 11 | opts.drawReady({x: x, y: y}); 12 | }, _drawReady = function (e) { 13 | _moveto(e); 14 | readydraw = true; 15 | } , _drawBegin = function (e) { 16 | e.preventDefault(); 17 | e.cancelBubble = true; 18 | if (begin) { 19 | var x = e.offsetX || e.changedTouches[0].clientX + e.layerX, y = e.offsetY || e.changedTouches[0].clientY + e.layerY; 20 | ctx.lineTo(x, y); 21 | ctx.stroke(); 22 | opts.drawBegin({x: x, y: y}); 23 | } 24 | } , _drawEnd = function (e) { 25 | begin = false; 26 | opts.drawEnd; 27 | ctx.closePath(); 28 | readydraw = false; 29 | }; 30 | 31 | return this.each(function () { 32 | var canvas = $(this); 33 | if (canvas[0].getContext) { 34 | ctx = canvas[0].getContext('2d'); 35 | ctx.strokeStyle = opts.BrushColor; 36 | ctx.lineWidth = opts.BrushWidth; 37 | ctx.lineJoin = opts.LineJoin; 38 | ctx.lineCap = opts.LineCap; 39 | 40 | } 41 | canvas.on("mousedown", _drawReady); 42 | canvas.on("mousemove", _drawBegin); 43 | canvas.on("mouseup", _drawEnd); 44 | canvas.on("mouseout", _drawEnd); 45 | canvas[0].addEventListener("touchstart", _drawReady, false); 46 | canvas[0].addEventListener("touchmove", _drawBegin, false); 47 | canvas[0].addEventListener("touchend", _drawEnd, false); 48 | }) 49 | 50 | }; 51 | var Methods = { 52 | SetColor: function (color) { 53 | ctx.strokeStyle = color; 54 | }, 55 | GetColor: function (color) { 56 | return ctx.strokeStyle; 57 | }, 58 | SetWidth: function (w) { 59 | ctx.lineWidth = w; 60 | }, 61 | SaveImage: function () { 62 | var data = ctx.canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); 63 | window.location.href = data; 64 | }, 65 | Eraser: function () { 66 | if (needRestore) 67 | ctx.save(); 68 | this.SetWidth(50); 69 | this.SetColor("#FFF"); 70 | }, 71 | Clear: function () { 72 | ctx.save(); 73 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 74 | ctx.restore(); 75 | needRestore = false; 76 | }, 77 | Pen: function () { 78 | if (needRestore) 79 | ctx.restore(); 80 | } 81 | } 82 | 83 | 84 | $.fn.MPaint.defaults = { 85 | BrushColor: "#fff", 86 | BrushWidth: 5, 87 | LineJoin: "round", 88 | LineCap: "round", 89 | //mousedown 90 | drawReady: function (e) { 91 | 92 | }, 93 | //Mousemove 94 | drawBegin: function (e) { 95 | 96 | }, 97 | //mouseup 98 | drawEnd: function (e) { 99 | 100 | } 101 | }; 102 | $.extend($.fn.MPaint, Methods); 103 | })(jQuery, window) -------------------------------------------------------------------------------- /public/stylesheets/colorpicker.css: -------------------------------------------------------------------------------- 1 | .colorpicker { 2 | width: 356px; 3 | height: 176px; 4 | overflow: hidden; 5 | position: absolute; 6 | background: url(../images/colorpicker_background.png); 7 | font-family: Arial, Helvetica, sans-serif; 8 | display: none; 9 | } 10 | .colorpicker_color { 11 | width: 150px; 12 | height: 150px; 13 | left: 14px; 14 | top: 13px; 15 | position: absolute; 16 | background: #f00; 17 | overflow: hidden; 18 | cursor: crosshair; 19 | } 20 | .colorpicker_color div { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | width: 150px; 25 | height: 150px; 26 | background: url(../images/colorpicker_overlay.png); 27 | } 28 | .colorpicker_color div div { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | width: 11px; 33 | height: 11px; 34 | overflow: hidden; 35 | background: url(../images/colorpicker_select.gif); 36 | margin: -5px 0 0 -5px; 37 | } 38 | .colorpicker_hue { 39 | position: absolute; 40 | top: 13px; 41 | left: 171px; 42 | width: 35px; 43 | height: 150px; 44 | cursor: n-resize; 45 | } 46 | .colorpicker_hue div { 47 | position: absolute; 48 | width: 35px; 49 | height: 9px; 50 | overflow: hidden; 51 | background: url(../images/colorpicker_indic.gif) left top; 52 | margin: -4px 0 0 0; 53 | left: 0px; 54 | } 55 | .colorpicker_new_color { 56 | position: absolute; 57 | width: 60px; 58 | height: 30px; 59 | left: 213px; 60 | top: 13px; 61 | background: #f00; 62 | } 63 | .colorpicker_current_color { 64 | position: absolute; 65 | width: 60px; 66 | height: 30px; 67 | left: 283px; 68 | top: 13px; 69 | background: #f00; 70 | } 71 | .colorpicker input { 72 | background-color: transparent; 73 | border: 1px solid transparent; 74 | position: absolute; 75 | font-size: 10px; 76 | font-family: Arial, Helvetica, sans-serif; 77 | color: #898989; 78 | top: 4px; 79 | right: 11px; 80 | text-align: right; 81 | margin: 0; 82 | padding: 0; 83 | height: 11px; 84 | } 85 | .colorpicker_hex { 86 | position: absolute; 87 | width: 72px; 88 | height: 22px; 89 | background: url(../images/colorpicker_hex.png) top; 90 | left: 212px; 91 | top: 142px; 92 | } 93 | .colorpicker_hex input { 94 | right: 6px; 95 | } 96 | .colorpicker_field { 97 | height: 22px; 98 | width: 62px; 99 | background-position: top; 100 | position: absolute; 101 | } 102 | .colorpicker_field span { 103 | position: absolute; 104 | width: 12px; 105 | height: 22px; 106 | overflow: hidden; 107 | top: 0; 108 | right: 0; 109 | cursor: n-resize; 110 | } 111 | .colorpicker_rgb_r { 112 | background-image: url(../images/colorpicker_rgb_r.png); 113 | top: 52px; 114 | left: 212px; 115 | } 116 | .colorpicker_rgb_g { 117 | background-image: url(../images/colorpicker_rgb_g.png); 118 | top: 82px; 119 | left: 212px; 120 | } 121 | .colorpicker_rgb_b { 122 | background-image: url(../images/colorpicker_rgb_b.png); 123 | top: 112px; 124 | left: 212px; 125 | } 126 | .colorpicker_hsb_h { 127 | background-image: url(../images/colorpicker_hsb_h.png); 128 | top: 52px; 129 | left: 282px; 130 | } 131 | .colorpicker_hsb_s { 132 | background-image: url(../images/colorpicker_hsb_s.png); 133 | top: 82px; 134 | left: 282px; 135 | } 136 | .colorpicker_hsb_b { 137 | background-image: url(../images/colorpicker_hsb_b.png); 138 | top: 112px; 139 | left: 282px; 140 | } 141 | .colorpicker_submit { 142 | position: absolute; 143 | width: 22px; 144 | height: 22px; 145 | background: url(../images/colorpicker_submit.png) top; 146 | left: 322px; 147 | top: 142px; 148 | overflow: hidden; 149 | } 150 | .colorpicker_focus { 151 | background-position: center; 152 | } 153 | .colorpicker_hex.colorpicker_focus { 154 | background-position: bottom; 155 | } 156 | .colorpicker_submit.colorpicker_focus { 157 | background-position: bottom; 158 | } 159 | .colorpicker_slider { 160 | background-position: bottom; 161 | } 162 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | 23 | 您的浏览器不支持,请使用chrome、firefox浏览器 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 | 35 |
36 |

用户列表

37 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 50 | 54 |
55 | 56 | 57 |
58 |
59 |
60 |
61 |
62 | 63 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /public/js/colorpicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Color picker 4 | * Author: Stefan Petre www.eyecon.ro 5 | * 6 | * Dual licensed under the MIT and GPL licenses 7 | * 8 | */ 9 | (function ($) { 10 | var ColorPicker = function () { 11 | var 12 | ids = {}, 13 | inAction, 14 | charMin = 65, 15 | visible, 16 | tpl = '
', 17 | defaults = { 18 | eventName: 'click', 19 | onShow: function () {}, 20 | onBeforeShow: function(){}, 21 | onHide: function () {}, 22 | onChange: function () {}, 23 | onSubmit: function () {}, 24 | color: 'ff0000', 25 | livePreview: true, 26 | flat: false 27 | }, 28 | fillRGBFields = function (hsb, cal) { 29 | var rgb = HSBToRGB(hsb); 30 | $(cal).data('colorpicker').fields 31 | .eq(1).val(rgb.r).end() 32 | .eq(2).val(rgb.g).end() 33 | .eq(3).val(rgb.b).end(); 34 | }, 35 | fillHSBFields = function (hsb, cal) { 36 | $(cal).data('colorpicker').fields 37 | .eq(4).val(hsb.h).end() 38 | .eq(5).val(hsb.s).end() 39 | .eq(6).val(hsb.b).end(); 40 | }, 41 | fillHexFields = function (hsb, cal) { 42 | $(cal).data('colorpicker').fields 43 | .eq(0).val(HSBToHex(hsb)).end(); 44 | }, 45 | setSelector = function (hsb, cal) { 46 | $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); 47 | $(cal).data('colorpicker').selectorIndic.css({ 48 | left: parseInt(150 * hsb.s/100, 10), 49 | top: parseInt(150 * (100-hsb.b)/100, 10) 50 | }); 51 | }, 52 | setHue = function (hsb, cal) { 53 | $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); 54 | }, 55 | setCurrentColor = function (hsb, cal) { 56 | $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); 57 | }, 58 | setNewColor = function (hsb, cal) { 59 | $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); 60 | }, 61 | keyDown = function (ev) { 62 | var pressedKey = ev.charCode || ev.keyCode || -1; 63 | if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { 64 | return false; 65 | } 66 | var cal = $(this).parent().parent(); 67 | if (cal.data('colorpicker').livePreview === true) { 68 | change.apply(this); 69 | } 70 | }, 71 | change = function (ev) { 72 | var cal = $(this).parent().parent(), col; 73 | if (this.parentNode.className.indexOf('_hex') > 0) { 74 | cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); 75 | } else if (this.parentNode.className.indexOf('_hsb') > 0) { 76 | cal.data('colorpicker').color = col = fixHSB({ 77 | h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), 78 | s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), 79 | b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) 80 | }); 81 | } else { 82 | cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ 83 | r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), 84 | g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), 85 | b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) 86 | })); 87 | } 88 | if (ev) { 89 | fillRGBFields(col, cal.get(0)); 90 | fillHexFields(col, cal.get(0)); 91 | fillHSBFields(col, cal.get(0)); 92 | } 93 | setSelector(col, cal.get(0)); 94 | setHue(col, cal.get(0)); 95 | setNewColor(col, cal.get(0)); 96 | cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); 97 | }, 98 | blur = function (ev) { 99 | var cal = $(this).parent().parent(); 100 | cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); 101 | }, 102 | focus = function () { 103 | charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; 104 | $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); 105 | $(this).parent().addClass('colorpicker_focus'); 106 | }, 107 | downIncrement = function (ev) { 108 | var field = $(this).parent().find('input').focus(); 109 | var current = { 110 | el: $(this).parent().addClass('colorpicker_slider'), 111 | max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), 112 | y: ev.pageY, 113 | field: field, 114 | val: parseInt(field.val(), 10), 115 | preview: $(this).parent().parent().data('colorpicker').livePreview 116 | }; 117 | $(document).bind('mouseup', current, upIncrement); 118 | $(document).bind('mousemove', current, moveIncrement); 119 | }, 120 | moveIncrement = function (ev) { 121 | ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); 122 | if (ev.data.preview) { 123 | change.apply(ev.data.field.get(0), [true]); 124 | } 125 | return false; 126 | }, 127 | upIncrement = function (ev) { 128 | change.apply(ev.data.field.get(0), [true]); 129 | ev.data.el.removeClass('colorpicker_slider').find('input').focus(); 130 | $(document).unbind('mouseup', upIncrement); 131 | $(document).unbind('mousemove', moveIncrement); 132 | return false; 133 | }, 134 | downHue = function (ev) { 135 | var current = { 136 | cal: $(this).parent(), 137 | y: $(this).offset().top 138 | }; 139 | current.preview = current.cal.data('colorpicker').livePreview; 140 | $(document).bind('mouseup', current, upHue); 141 | $(document).bind('mousemove', current, moveHue); 142 | }, 143 | moveHue = function (ev) { 144 | change.apply( 145 | ev.data.cal.data('colorpicker') 146 | .fields 147 | .eq(4) 148 | .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) 149 | .get(0), 150 | [ev.data.preview] 151 | ); 152 | return false; 153 | }, 154 | upHue = function (ev) { 155 | fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 156 | fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 157 | $(document).unbind('mouseup', upHue); 158 | $(document).unbind('mousemove', moveHue); 159 | return false; 160 | }, 161 | downSelector = function (ev) { 162 | var current = { 163 | cal: $(this).parent(), 164 | pos: $(this).offset() 165 | }; 166 | current.preview = current.cal.data('colorpicker').livePreview; 167 | $(document).bind('mouseup', current, upSelector); 168 | $(document).bind('mousemove', current, moveSelector); 169 | }, 170 | moveSelector = function (ev) { 171 | change.apply( 172 | ev.data.cal.data('colorpicker') 173 | .fields 174 | .eq(6) 175 | .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) 176 | .end() 177 | .eq(5) 178 | .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) 179 | .get(0), 180 | [ev.data.preview] 181 | ); 182 | return false; 183 | }, 184 | upSelector = function (ev) { 185 | fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 186 | fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 187 | $(document).unbind('mouseup', upSelector); 188 | $(document).unbind('mousemove', moveSelector); 189 | return false; 190 | }, 191 | enterSubmit = function (ev) { 192 | $(this).addClass('colorpicker_focus'); 193 | }, 194 | leaveSubmit = function (ev) { 195 | $(this).removeClass('colorpicker_focus'); 196 | }, 197 | clickSubmit = function (ev) { 198 | var cal = $(this).parent(); 199 | var col = cal.data('colorpicker').color; 200 | cal.data('colorpicker').origColor = col; 201 | setCurrentColor(col, cal.get(0)); 202 | cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); 203 | }, 204 | show = function (ev) { 205 | var cal = $('#' + $(this).data('colorpickerId')); 206 | cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); 207 | var pos = $(this).offset(); 208 | var viewPort = getViewport(); 209 | var top = pos.top + this.offsetHeight; 210 | var left = pos.left; 211 | if (top + 176 > viewPort.t + viewPort.h) { 212 | top -= this.offsetHeight + 176; 213 | } 214 | if (left + 356 > viewPort.l + viewPort.w) { 215 | left -= 356; 216 | } 217 | cal.css({left: left + 'px', top: top + 'px'}); 218 | if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { 219 | cal.show(); 220 | } 221 | $(document).bind('mousedown', {cal: cal}, hide); 222 | return false; 223 | }, 224 | hide = function (ev) { 225 | if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { 226 | if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { 227 | ev.data.cal.hide(); 228 | } 229 | $(document).unbind('mousedown', hide); 230 | } 231 | }, 232 | isChildOf = function(parentEl, el, container) { 233 | if (parentEl == el) { 234 | return true; 235 | } 236 | if (parentEl.contains) { 237 | return parentEl.contains(el); 238 | } 239 | if ( parentEl.compareDocumentPosition ) { 240 | return !!(parentEl.compareDocumentPosition(el) & 16); 241 | } 242 | var prEl = el.parentNode; 243 | while(prEl && prEl != container) { 244 | if (prEl == parentEl) 245 | return true; 246 | prEl = prEl.parentNode; 247 | } 248 | return false; 249 | }, 250 | getViewport = function () { 251 | var m = document.compatMode == 'CSS1Compat'; 252 | return { 253 | l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), 254 | t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), 255 | w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), 256 | h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) 257 | }; 258 | }, 259 | fixHSB = function (hsb) { 260 | return { 261 | h: Math.min(360, Math.max(0, hsb.h)), 262 | s: Math.min(100, Math.max(0, hsb.s)), 263 | b: Math.min(100, Math.max(0, hsb.b)) 264 | }; 265 | }, 266 | fixRGB = function (rgb) { 267 | return { 268 | r: Math.min(255, Math.max(0, rgb.r)), 269 | g: Math.min(255, Math.max(0, rgb.g)), 270 | b: Math.min(255, Math.max(0, rgb.b)) 271 | }; 272 | }, 273 | fixHex = function (hex) { 274 | var len = 6 - hex.length; 275 | if (len > 0) { 276 | var o = []; 277 | for (var i=0; i -1) ? hex.substring(1) : hex), 16); 287 | return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; 288 | }, 289 | HexToHSB = function (hex) { 290 | return RGBToHSB(HexToRGB(hex)); 291 | }, 292 | RGBToHSB = function (rgb) { 293 | var hsb = { 294 | h: 0, 295 | s: 0, 296 | b: 0 297 | }; 298 | var min = Math.min(rgb.r, rgb.g, rgb.b); 299 | var max = Math.max(rgb.r, rgb.g, rgb.b); 300 | var delta = max - min; 301 | hsb.b = max; 302 | if (max != 0) { 303 | 304 | } 305 | hsb.s = max != 0 ? 255 * delta / max : 0; 306 | if (hsb.s != 0) { 307 | if (rgb.r == max) { 308 | hsb.h = (rgb.g - rgb.b) / delta; 309 | } else if (rgb.g == max) { 310 | hsb.h = 2 + (rgb.b - rgb.r) / delta; 311 | } else { 312 | hsb.h = 4 + (rgb.r - rgb.g) / delta; 313 | } 314 | } else { 315 | hsb.h = -1; 316 | } 317 | hsb.h *= 60; 318 | if (hsb.h < 0) { 319 | hsb.h += 360; 320 | } 321 | hsb.s *= 100/255; 322 | hsb.b *= 100/255; 323 | return hsb; 324 | }, 325 | HSBToRGB = function (hsb) { 326 | var rgb = {}; 327 | var h = Math.round(hsb.h); 328 | var s = Math.round(hsb.s*255/100); 329 | var v = Math.round(hsb.b*255/100); 330 | if(s == 0) { 331 | rgb.r = rgb.g = rgb.b = v; 332 | } else { 333 | var t1 = v; 334 | var t2 = (255-s)*v/255; 335 | var t3 = (t1-t2)*(h%60)/60; 336 | if(h==360) h = 0; 337 | if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} 338 | else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} 339 | else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} 340 | else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} 341 | else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} 342 | else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} 343 | else {rgb.r=0; rgb.g=0; rgb.b=0} 344 | } 345 | return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; 346 | }, 347 | RGBToHex = function (rgb) { 348 | var hex = [ 349 | rgb.r.toString(16), 350 | rgb.g.toString(16), 351 | rgb.b.toString(16) 352 | ]; 353 | $.each(hex, function (nr, val) { 354 | if (val.length == 1) { 355 | hex[nr] = '0' + val; 356 | } 357 | }); 358 | return hex.join(''); 359 | }, 360 | HSBToHex = function (hsb) { 361 | return RGBToHex(HSBToRGB(hsb)); 362 | }, 363 | restoreOriginal = function () { 364 | var cal = $(this).parent(); 365 | var col = cal.data('colorpicker').origColor; 366 | cal.data('colorpicker').color = col; 367 | fillRGBFields(col, cal.get(0)); 368 | fillHexFields(col, cal.get(0)); 369 | fillHSBFields(col, cal.get(0)); 370 | setSelector(col, cal.get(0)); 371 | setHue(col, cal.get(0)); 372 | setNewColor(col, cal.get(0)); 373 | }; 374 | return { 375 | init: function (opt) { 376 | opt = $.extend({}, defaults, opt||{}); 377 | if (typeof opt.color == 'string') { 378 | opt.color = HexToHSB(opt.color); 379 | } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { 380 | opt.color = RGBToHSB(opt.color); 381 | } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { 382 | opt.color = fixHSB(opt.color); 383 | } else { 384 | return this; 385 | } 386 | return this.each(function () { 387 | if (!$(this).data('colorpickerId')) { 388 | var options = $.extend({}, opt); 389 | options.origColor = opt.color; 390 | var id = 'collorpicker_' + parseInt(Math.random() * 1000); 391 | $(this).data('colorpickerId', id); 392 | var cal = $(tpl).attr('id', id); 393 | if (options.flat) { 394 | cal.appendTo(this).show(); 395 | } else { 396 | cal.appendTo(document.body); 397 | } 398 | options.fields = cal 399 | .find('input') 400 | .bind('keyup', keyDown) 401 | .bind('change', change) 402 | .bind('blur', blur) 403 | .bind('focus', focus); 404 | cal 405 | .find('span').bind('mousedown', downIncrement).end() 406 | .find('>div.colorpicker_current_color').bind('click', restoreOriginal); 407 | options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); 408 | options.selectorIndic = options.selector.find('div div'); 409 | options.el = this; 410 | options.hue = cal.find('div.colorpicker_hue div'); 411 | cal.find('div.colorpicker_hue').bind('mousedown', downHue); 412 | options.newColor = cal.find('div.colorpicker_new_color'); 413 | options.currentColor = cal.find('div.colorpicker_current_color'); 414 | cal.data('colorpicker', options); 415 | cal.find('div.colorpicker_submit') 416 | .bind('mouseenter', enterSubmit) 417 | .bind('mouseleave', leaveSubmit) 418 | .bind('click', clickSubmit); 419 | fillRGBFields(options.color, cal.get(0)); 420 | fillHSBFields(options.color, cal.get(0)); 421 | fillHexFields(options.color, cal.get(0)); 422 | setHue(options.color, cal.get(0)); 423 | setSelector(options.color, cal.get(0)); 424 | setCurrentColor(options.color, cal.get(0)); 425 | setNewColor(options.color, cal.get(0)); 426 | if (options.flat) { 427 | cal.css({ 428 | position: 'relative', 429 | display: 'block' 430 | }); 431 | } else { 432 | $(this).bind(options.eventName, show); 433 | } 434 | } 435 | }); 436 | }, 437 | showPicker: function() { 438 | return this.each( function () { 439 | if ($(this).data('colorpickerId')) { 440 | show.apply(this); 441 | } 442 | }); 443 | }, 444 | hidePicker: function() { 445 | return this.each( function () { 446 | if ($(this).data('colorpickerId')) { 447 | $('#' + $(this).data('colorpickerId')).hide(); 448 | } 449 | }); 450 | }, 451 | setColor: function(col) { 452 | if (typeof col == 'string') { 453 | col = HexToHSB(col); 454 | } else if (col.r != undefined && col.g != undefined && col.b != undefined) { 455 | col = RGBToHSB(col); 456 | } else if (col.h != undefined && col.s != undefined && col.b != undefined) { 457 | col = fixHSB(col); 458 | } else { 459 | return this; 460 | } 461 | return this.each(function(){ 462 | if ($(this).data('colorpickerId')) { 463 | var cal = $('#' + $(this).data('colorpickerId')); 464 | cal.data('colorpicker').color = col; 465 | cal.data('colorpicker').origColor = col; 466 | fillRGBFields(col, cal.get(0)); 467 | fillHSBFields(col, cal.get(0)); 468 | fillHexFields(col, cal.get(0)); 469 | setHue(col, cal.get(0)); 470 | setSelector(col, cal.get(0)); 471 | setCurrentColor(col, cal.get(0)); 472 | setNewColor(col, cal.get(0)); 473 | } 474 | }); 475 | } 476 | }; 477 | }(); 478 | $.fn.extend({ 479 | ColorPicker: ColorPicker.init, 480 | ColorPickerHide: ColorPicker.hidePicker, 481 | ColorPickerShow: ColorPicker.showPicker, 482 | ColorPickerSetColor: ColorPicker.setColor 483 | }); 484 | })(jQuery) -------------------------------------------------------------------------------- /public/js/excanvas.js: -------------------------------------------------------------------------------- 1 | // Copyright 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | // Known Issues: 17 | // 18 | // * Patterns are not implemented. 19 | // * Radial gradient are not implemented. The VML version of these look very 20 | // different from the canvas one. 21 | // * Clipping paths are not implemented. 22 | // * Coordsize. The width and height attribute have higher priority than the 23 | // width and height style values which isn't correct. 24 | // * Painting mode isn't implemented. 25 | // * Canvas width/height should is using content-box by default. IE in 26 | // Quirks mode will draw the canvas using border-box. Either change your 27 | // doctype to HTML5 28 | // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) 29 | // or use Box Sizing Behavior from WebFX 30 | // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) 31 | // * Non uniform scaling does not correctly scale strokes. 32 | // * Optimize. There is always room for speed improvements. 33 | 34 | // Only add this code if we do not already have a canvas implementation 35 | if (!document.createElement('canvas').getContext) { 36 | 37 | (function() { 38 | 39 | // alias some functions to make (compiled) code shorter 40 | var m = Math; 41 | var mr = m.round; 42 | var ms = m.sin; 43 | var mc = m.cos; 44 | var abs = m.abs; 45 | var sqrt = m.sqrt; 46 | 47 | // this is used for sub pixel precision 48 | var Z = 10; 49 | var Z2 = Z / 2; 50 | 51 | /** 52 | * This funtion is assigned to the elements as element.getContext(). 53 | * @this {HTMLElement} 54 | * @return {CanvasRenderingContext2D_} 55 | */ 56 | function getContext() { 57 | return this.context_ || 58 | (this.context_ = new CanvasRenderingContext2D_(this)); 59 | } 60 | 61 | var slice = Array.prototype.slice; 62 | 63 | /** 64 | * Binds a function to an object. The returned function will always use the 65 | * passed in {@code obj} as {@code this}. 66 | * 67 | * Example: 68 | * 69 | * g = bind(f, obj, a, b) 70 | * g(c, d) // will do f.call(obj, a, b, c, d) 71 | * 72 | * @param {Function} f The function to bind the object to 73 | * @param {Object} obj The object that should act as this when the function 74 | * is called 75 | * @param {*} var_args Rest arguments that will be used as the initial 76 | * arguments when the function is called 77 | * @return {Function} A new function that has bound this 78 | */ 79 | function bind(f, obj, var_args) { 80 | var a = slice.call(arguments, 2); 81 | return function() { 82 | return f.apply(obj, a.concat(slice.call(arguments))); 83 | }; 84 | } 85 | 86 | var G_vmlCanvasManager_ = { 87 | init: function(opt_doc) { 88 | if (/MSIE/.test(navigator.userAgent) && !window.opera) { 89 | var doc = opt_doc || document; 90 | // Create a dummy element so that IE will allow canvas elements to be 91 | // recognized. 92 | doc.createElement('canvas'); 93 | doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); 94 | } 95 | }, 96 | 97 | init_: function(doc) { 98 | // create xmlns 99 | if (!doc.namespaces['g_vml_']) { 100 | doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', 101 | '#default#VML'); 102 | 103 | } 104 | if (!doc.namespaces['g_o_']) { 105 | doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', 106 | '#default#VML'); 107 | } 108 | 109 | // Setup default CSS. Only add one style sheet per document 110 | if (!doc.styleSheets['ex_canvas_']) { 111 | var ss = doc.createStyleSheet(); 112 | ss.owningElement.id = 'ex_canvas_'; 113 | ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + 114 | // default size is 300x150 in Gecko and Opera 115 | 'text-align:left;width:300px;height:150px}' + 116 | 'g_vml_\\:*{behavior:url(#default#VML)}' + 117 | 'g_o_\\:*{behavior:url(#default#VML)}'; 118 | 119 | } 120 | 121 | // find all canvas elements 122 | var els = doc.getElementsByTagName('canvas'); 123 | for (var i = 0; i < els.length; i++) { 124 | this.initElement(els[i]); 125 | } 126 | }, 127 | 128 | /** 129 | * Public initializes a canvas element so that it can be used as canvas 130 | * element from now on. This is called automatically before the page is 131 | * loaded but if you are creating elements using createElement you need to 132 | * make sure this is called on the element. 133 | * @param {HTMLElement} el The canvas element to initialize. 134 | * @return {HTMLElement} the element that was created. 135 | */ 136 | initElement: function(el) { 137 | if (!el.getContext) { 138 | 139 | el.getContext = getContext; 140 | 141 | // Remove fallback content. There is no way to hide text nodes so we 142 | // just remove all childNodes. We could hide all elements and remove 143 | // text nodes but who really cares about the fallback content. 144 | el.innerHTML = ''; 145 | 146 | // do not use inline function because that will leak memory 147 | el.attachEvent('onpropertychange', onPropertyChange); 148 | el.attachEvent('onresize', onResize); 149 | 150 | var attrs = el.attributes; 151 | if (attrs.width && attrs.width.specified) { 152 | // TODO: use runtimeStyle and coordsize 153 | // el.getContext().setWidth_(attrs.width.nodeValue); 154 | el.style.width = attrs.width.nodeValue + 'px'; 155 | } else { 156 | el.width = el.clientWidth; 157 | } 158 | if (attrs.height && attrs.height.specified) { 159 | // TODO: use runtimeStyle and coordsize 160 | // el.getContext().setHeight_(attrs.height.nodeValue); 161 | el.style.height = attrs.height.nodeValue + 'px'; 162 | } else { 163 | el.height = el.clientHeight; 164 | } 165 | //el.getContext().setCoordsize_() 166 | } 167 | return el; 168 | } 169 | }; 170 | 171 | function onPropertyChange(e) { 172 | var el = e.srcElement; 173 | 174 | switch (e.propertyName) { 175 | case 'width': 176 | el.style.width = el.attributes.width.nodeValue + 'px'; 177 | el.getContext().clearRect(); 178 | break; 179 | case 'height': 180 | el.style.height = el.attributes.height.nodeValue + 'px'; 181 | el.getContext().clearRect(); 182 | break; 183 | } 184 | } 185 | 186 | function onResize(e) { 187 | var el = e.srcElement; 188 | if (el.firstChild) { 189 | el.firstChild.style.width = el.clientWidth + 'px'; 190 | el.firstChild.style.height = el.clientHeight + 'px'; 191 | } 192 | } 193 | 194 | G_vmlCanvasManager_.init(); 195 | 196 | // precompute "00" to "FF" 197 | var dec2hex = []; 198 | for (var i = 0; i < 16; i++) { 199 | for (var j = 0; j < 16; j++) { 200 | dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); 201 | } 202 | } 203 | 204 | function createMatrixIdentity() { 205 | return [ 206 | [1, 0, 0], 207 | [0, 1, 0], 208 | [0, 0, 1] 209 | ]; 210 | } 211 | 212 | function matrixMultiply(m1, m2) { 213 | var result = createMatrixIdentity(); 214 | 215 | for (var x = 0; x < 3; x++) { 216 | for (var y = 0; y < 3; y++) { 217 | var sum = 0; 218 | 219 | for (var z = 0; z < 3; z++) { 220 | sum += m1[x][z] * m2[z][y]; 221 | } 222 | 223 | result[x][y] = sum; 224 | } 225 | } 226 | return result; 227 | } 228 | 229 | function copyState(o1, o2) { 230 | o2.fillStyle = o1.fillStyle; 231 | o2.lineCap = o1.lineCap; 232 | o2.lineJoin = o1.lineJoin; 233 | o2.lineWidth = o1.lineWidth; 234 | o2.miterLimit = o1.miterLimit; 235 | o2.shadowBlur = o1.shadowBlur; 236 | o2.shadowColor = o1.shadowColor; 237 | o2.shadowOffsetX = o1.shadowOffsetX; 238 | o2.shadowOffsetY = o1.shadowOffsetY; 239 | o2.strokeStyle = o1.strokeStyle; 240 | o2.globalAlpha = o1.globalAlpha; 241 | o2.arcScaleX_ = o1.arcScaleX_; 242 | o2.arcScaleY_ = o1.arcScaleY_; 243 | o2.lineScale_ = o1.lineScale_; 244 | } 245 | 246 | function processStyle(styleString) { 247 | var str, alpha = 1; 248 | 249 | styleString = String(styleString); 250 | if (styleString.substring(0, 3) == 'rgb') { 251 | var start = styleString.indexOf('(', 3); 252 | var end = styleString.indexOf(')', start + 1); 253 | var guts = styleString.substring(start + 1, end).split(','); 254 | 255 | str = '#'; 256 | for (var i = 0; i < 3; i++) { 257 | str += dec2hex[Number(guts[i])]; 258 | } 259 | 260 | if (guts.length == 4 && styleString.substr(3, 1) == 'a') { 261 | alpha = guts[3]; 262 | } 263 | } else { 264 | str = styleString; 265 | } 266 | 267 | return {color: str, alpha: alpha}; 268 | } 269 | 270 | function processLineCap(lineCap) { 271 | switch (lineCap) { 272 | case 'butt': 273 | return 'flat'; 274 | case 'round': 275 | return 'round'; 276 | case 'square': 277 | default: 278 | return 'square'; 279 | } 280 | } 281 | 282 | /** 283 | * This class implements CanvasRenderingContext2D interface as described by 284 | * the WHATWG. 285 | * @param {HTMLElement} surfaceElement The element that the 2D context should 286 | * be associated with 287 | */ 288 | function CanvasRenderingContext2D_(surfaceElement) { 289 | this.m_ = createMatrixIdentity(); 290 | 291 | this.mStack_ = []; 292 | this.aStack_ = []; 293 | this.currentPath_ = []; 294 | 295 | // Canvas context properties 296 | this.strokeStyle = '#000'; 297 | this.fillStyle = '#000'; 298 | 299 | this.lineWidth = 1; 300 | this.lineJoin = 'miter'; 301 | this.lineCap = 'butt'; 302 | this.miterLimit = Z * 1; 303 | this.globalAlpha = 1; 304 | this.canvas = surfaceElement; 305 | 306 | var el = surfaceElement.ownerDocument.createElement('div'); 307 | el.style.width = surfaceElement.clientWidth + 'px'; 308 | el.style.height = surfaceElement.clientHeight + 'px'; 309 | el.style.overflow = 'hidden'; 310 | el.style.position = 'absolute'; 311 | surfaceElement.appendChild(el); 312 | 313 | this.element_ = el; 314 | this.arcScaleX_ = 1; 315 | this.arcScaleY_ = 1; 316 | this.lineScale_ = 1; 317 | } 318 | 319 | var contextPrototype = CanvasRenderingContext2D_.prototype; 320 | contextPrototype.clearRect = function() { 321 | this.element_.innerHTML = ''; 322 | }; 323 | 324 | contextPrototype.beginPath = function() { 325 | // TODO: Branch current matrix so that save/restore has no effect 326 | // as per safari docs. 327 | this.currentPath_ = []; 328 | }; 329 | 330 | contextPrototype.moveTo = function(aX, aY) { 331 | var p = this.getCoords_(aX, aY); 332 | this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); 333 | this.currentX_ = p.x; 334 | this.currentY_ = p.y; 335 | }; 336 | 337 | contextPrototype.lineTo = function(aX, aY) { 338 | var p = this.getCoords_(aX, aY); 339 | this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); 340 | 341 | this.currentX_ = p.x; 342 | this.currentY_ = p.y; 343 | }; 344 | 345 | contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, 346 | aCP2x, aCP2y, 347 | aX, aY) { 348 | var p = this.getCoords_(aX, aY); 349 | var cp1 = this.getCoords_(aCP1x, aCP1y); 350 | var cp2 = this.getCoords_(aCP2x, aCP2y); 351 | bezierCurveTo(this, cp1, cp2, p); 352 | }; 353 | 354 | // Helper function that takes the already fixed cordinates. 355 | function bezierCurveTo(self, cp1, cp2, p) { 356 | self.currentPath_.push({ 357 | type: 'bezierCurveTo', 358 | cp1x: cp1.x, 359 | cp1y: cp1.y, 360 | cp2x: cp2.x, 361 | cp2y: cp2.y, 362 | x: p.x, 363 | y: p.y 364 | }); 365 | self.currentX_ = p.x; 366 | self.currentY_ = p.y; 367 | } 368 | 369 | contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { 370 | // the following is lifted almost directly from 371 | // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes 372 | 373 | var cp = this.getCoords_(aCPx, aCPy); 374 | var p = this.getCoords_(aX, aY); 375 | 376 | var cp1 = { 377 | x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), 378 | y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) 379 | }; 380 | var cp2 = { 381 | x: cp1.x + (p.x - this.currentX_) / 3.0, 382 | y: cp1.y + (p.y - this.currentY_) / 3.0 383 | }; 384 | 385 | bezierCurveTo(this, cp1, cp2, p); 386 | }; 387 | 388 | contextPrototype.arc = function(aX, aY, aRadius, 389 | aStartAngle, aEndAngle, aClockwise) { 390 | aRadius *= Z; 391 | var arcType = aClockwise ? 'at' : 'wa'; 392 | 393 | var xStart = aX + mc(aStartAngle) * aRadius - Z2; 394 | var yStart = aY + ms(aStartAngle) * aRadius - Z2; 395 | 396 | var xEnd = aX + mc(aEndAngle) * aRadius - Z2; 397 | var yEnd = aY + ms(aEndAngle) * aRadius - Z2; 398 | 399 | // IE won't render arches drawn counter clockwise if xStart == xEnd. 400 | if (xStart == xEnd && !aClockwise) { 401 | xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something 402 | // that can be represented in binary 403 | } 404 | 405 | var p = this.getCoords_(aX, aY); 406 | var pStart = this.getCoords_(xStart, yStart); 407 | var pEnd = this.getCoords_(xEnd, yEnd); 408 | 409 | this.currentPath_.push({type: arcType, 410 | x: p.x, 411 | y: p.y, 412 | radius: aRadius, 413 | xStart: pStart.x, 414 | yStart: pStart.y, 415 | xEnd: pEnd.x, 416 | yEnd: pEnd.y}); 417 | 418 | }; 419 | 420 | contextPrototype.rect = function(aX, aY, aWidth, aHeight) { 421 | this.moveTo(aX, aY); 422 | this.lineTo(aX + aWidth, aY); 423 | this.lineTo(aX + aWidth, aY + aHeight); 424 | this.lineTo(aX, aY + aHeight); 425 | this.closePath(); 426 | }; 427 | 428 | contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { 429 | var oldPath = this.currentPath_; 430 | this.beginPath(); 431 | 432 | this.moveTo(aX, aY); 433 | this.lineTo(aX + aWidth, aY); 434 | this.lineTo(aX + aWidth, aY + aHeight); 435 | this.lineTo(aX, aY + aHeight); 436 | this.closePath(); 437 | this.stroke(); 438 | 439 | this.currentPath_ = oldPath; 440 | }; 441 | 442 | contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { 443 | var oldPath = this.currentPath_; 444 | this.beginPath(); 445 | 446 | this.moveTo(aX, aY); 447 | this.lineTo(aX + aWidth, aY); 448 | this.lineTo(aX + aWidth, aY + aHeight); 449 | this.lineTo(aX, aY + aHeight); 450 | this.closePath(); 451 | this.fill(); 452 | 453 | this.currentPath_ = oldPath; 454 | }; 455 | 456 | contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { 457 | var gradient = new CanvasGradient_('gradient'); 458 | gradient.x0_ = aX0; 459 | gradient.y0_ = aY0; 460 | gradient.x1_ = aX1; 461 | gradient.y1_ = aY1; 462 | return gradient; 463 | }; 464 | 465 | contextPrototype.createRadialGradient = function(aX0, aY0, aR0, 466 | aX1, aY1, aR1) { 467 | var gradient = new CanvasGradient_('gradientradial'); 468 | gradient.x0_ = aX0; 469 | gradient.y0_ = aY0; 470 | gradient.r0_ = aR0; 471 | gradient.x1_ = aX1; 472 | gradient.y1_ = aY1; 473 | gradient.r1_ = aR1; 474 | return gradient; 475 | }; 476 | 477 | contextPrototype.drawImage = function(image, var_args) { 478 | var dx, dy, dw, dh, sx, sy, sw, sh; 479 | 480 | // to find the original width we overide the width and height 481 | var oldRuntimeWidth = image.runtimeStyle.width; 482 | var oldRuntimeHeight = image.runtimeStyle.height; 483 | image.runtimeStyle.width = 'auto'; 484 | image.runtimeStyle.height = 'auto'; 485 | 486 | // get the original size 487 | var w = image.width; 488 | var h = image.height; 489 | 490 | // and remove overides 491 | image.runtimeStyle.width = oldRuntimeWidth; 492 | image.runtimeStyle.height = oldRuntimeHeight; 493 | 494 | if (arguments.length == 3) { 495 | dx = arguments[1]; 496 | dy = arguments[2]; 497 | sx = sy = 0; 498 | sw = dw = w; 499 | sh = dh = h; 500 | } else if (arguments.length == 5) { 501 | dx = arguments[1]; 502 | dy = arguments[2]; 503 | dw = arguments[3]; 504 | dh = arguments[4]; 505 | sx = sy = 0; 506 | sw = w; 507 | sh = h; 508 | } else if (arguments.length == 9) { 509 | sx = arguments[1]; 510 | sy = arguments[2]; 511 | sw = arguments[3]; 512 | sh = arguments[4]; 513 | dx = arguments[5]; 514 | dy = arguments[6]; 515 | dw = arguments[7]; 516 | dh = arguments[8]; 517 | } else { 518 | throw Error('Invalid number of arguments'); 519 | } 520 | 521 | var d = this.getCoords_(dx, dy); 522 | 523 | var w2 = sw / 2; 524 | var h2 = sh / 2; 525 | 526 | var vmlStr = []; 527 | 528 | var W = 10; 529 | var H = 10; 530 | 531 | // For some reason that I've now forgotten, using divs didn't work 532 | vmlStr.push(' ' , 571 | '', 579 | ''); 580 | 581 | this.element_.insertAdjacentHTML('BeforeEnd', 582 | vmlStr.join('')); 583 | }; 584 | 585 | contextPrototype.stroke = function(aFill) { 586 | var lineStr = []; 587 | var lineOpen = false; 588 | var a = processStyle(aFill ? this.fillStyle : this.strokeStyle); 589 | var color = a.color; 590 | var opacity = a.alpha * this.globalAlpha; 591 | 592 | var W = 10; 593 | var H = 10; 594 | 595 | lineStr.push(''); 662 | 663 | if (!aFill) { 664 | var lineWidth = this.lineScale_ * this.lineWidth; 665 | 666 | // VML cannot correctly render a line if the width is less than 1px. 667 | // In that case, we dilute the color to make the line look thinner. 668 | if (lineWidth < 1) { 669 | opacity *= lineWidth; 670 | } 671 | 672 | lineStr.push( 673 | '' 680 | ); 681 | } else if (typeof this.fillStyle == 'object') { 682 | var fillStyle = this.fillStyle; 683 | var angle = 0; 684 | var focus = {x: 0, y: 0}; 685 | 686 | // additional offset 687 | var shift = 0; 688 | // scale factor for offset 689 | var expansion = 1; 690 | 691 | if (fillStyle.type_ == 'gradient') { 692 | var x0 = fillStyle.x0_ / this.arcScaleX_; 693 | var y0 = fillStyle.y0_ / this.arcScaleY_; 694 | var x1 = fillStyle.x1_ / this.arcScaleX_; 695 | var y1 = fillStyle.y1_ / this.arcScaleY_; 696 | var p0 = this.getCoords_(x0, y0); 697 | var p1 = this.getCoords_(x1, y1); 698 | var dx = p1.x - p0.x; 699 | var dy = p1.y - p0.y; 700 | angle = Math.atan2(dx, dy) * 180 / Math.PI; 701 | 702 | // The angle should be a non-negative number. 703 | if (angle < 0) { 704 | angle += 360; 705 | } 706 | 707 | // Very small angles produce an unexpected result because they are 708 | // converted to a scientific notation string. 709 | if (angle < 1e-6) { 710 | angle = 0; 711 | } 712 | } else { 713 | var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_); 714 | var width = max.x - min.x; 715 | var height = max.y - min.y; 716 | focus = { 717 | x: (p0.x - min.x) / width, 718 | y: (p0.y - min.y) / height 719 | }; 720 | 721 | width /= this.arcScaleX_ * Z; 722 | height /= this.arcScaleY_ * Z; 723 | var dimension = m.max(width, height); 724 | shift = 2 * fillStyle.r0_ / dimension; 725 | expansion = 2 * fillStyle.r1_ / dimension - shift; 726 | } 727 | 728 | // We need to sort the color stops in ascending order by offset, 729 | // otherwise IE won't interpret it correctly. 730 | var stops = fillStyle.colors_; 731 | stops.sort(function(cs1, cs2) { 732 | return cs1.offset - cs2.offset; 733 | }); 734 | 735 | var length = stops.length; 736 | var color1 = stops[0].color; 737 | var color2 = stops[length - 1].color; 738 | var opacity1 = stops[0].alpha * this.globalAlpha; 739 | var opacity2 = stops[length - 1].alpha * this.globalAlpha; 740 | 741 | var colors = []; 742 | for (var i = 0; i < length; i++) { 743 | var stop = stops[i]; 744 | colors.push(stop.offset * expansion + shift + ' ' + stop.color); 745 | } 746 | 747 | // When colors attribute is used, the meanings of opacity and o:opacity2 748 | // are reversed. 749 | lineStr.push(''); 758 | } else { 759 | lineStr.push(''); 761 | } 762 | 763 | lineStr.push(''); 764 | 765 | this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); 766 | }; 767 | 768 | contextPrototype.fill = function() { 769 | this.stroke(true); 770 | } 771 | 772 | contextPrototype.closePath = function() { 773 | this.currentPath_.push({type: 'close'}); 774 | }; 775 | 776 | /** 777 | * @private 778 | */ 779 | contextPrototype.getCoords_ = function(aX, aY) { 780 | var m = this.m_; 781 | return { 782 | x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, 783 | y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 784 | } 785 | }; 786 | 787 | contextPrototype.save = function() { 788 | var o = {}; 789 | copyState(this, o); 790 | this.aStack_.push(o); 791 | this.mStack_.push(this.m_); 792 | this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); 793 | }; 794 | 795 | contextPrototype.restore = function() { 796 | copyState(this.aStack_.pop(), this); 797 | this.m_ = this.mStack_.pop(); 798 | }; 799 | 800 | function matrixIsFinite(m) { 801 | for (var j = 0; j < 3; j++) { 802 | for (var k = 0; k < 2; k++) { 803 | if (!isFinite(m[j][k]) || isNaN(m[j][k])) { 804 | return false; 805 | } 806 | } 807 | } 808 | return true; 809 | } 810 | 811 | function setM(ctx, m, updateLineScale) { 812 | if (!matrixIsFinite(m)) { 813 | return; 814 | } 815 | ctx.m_ = m; 816 | 817 | if (updateLineScale) { 818 | // Get the line scale. 819 | // Determinant of this.m_ means how much the area is enlarged by the 820 | // transformation. So its square root can be used as a scale factor 821 | // for width. 822 | var det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; 823 | ctx.lineScale_ = sqrt(abs(det)); 824 | } 825 | } 826 | 827 | contextPrototype.translate = function(aX, aY) { 828 | var m1 = [ 829 | [1, 0, 0], 830 | [0, 1, 0], 831 | [aX, aY, 1] 832 | ]; 833 | 834 | setM(this, matrixMultiply(m1, this.m_), false); 835 | }; 836 | 837 | contextPrototype.rotate = function(aRot) { 838 | var c = mc(aRot); 839 | var s = ms(aRot); 840 | 841 | var m1 = [ 842 | [c, s, 0], 843 | [-s, c, 0], 844 | [0, 0, 1] 845 | ]; 846 | 847 | setM(this, matrixMultiply(m1, this.m_), false); 848 | }; 849 | 850 | contextPrototype.scale = function(aX, aY) { 851 | this.arcScaleX_ *= aX; 852 | this.arcScaleY_ *= aY; 853 | var m1 = [ 854 | [aX, 0, 0], 855 | [0, aY, 0], 856 | [0, 0, 1] 857 | ]; 858 | 859 | setM(this, matrixMultiply(m1, this.m_), true); 860 | }; 861 | 862 | contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) { 863 | var m1 = [ 864 | [m11, m12, 0], 865 | [m21, m22, 0], 866 | [dx, dy, 1] 867 | ]; 868 | 869 | setM(this, matrixMultiply(m1, this.m_), true); 870 | }; 871 | 872 | contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) { 873 | var m = [ 874 | [m11, m12, 0], 875 | [m21, m22, 0], 876 | [dx, dy, 1] 877 | ]; 878 | 879 | setM(this, m, true); 880 | }; 881 | 882 | /******** STUBS ********/ 883 | contextPrototype.clip = function() { 884 | // TODO: Implement 885 | }; 886 | 887 | contextPrototype.arcTo = function() { 888 | // TODO: Implement 889 | }; 890 | 891 | contextPrototype.createPattern = function() { 892 | return new CanvasPattern_; 893 | }; 894 | 895 | // Gradient / Pattern Stubs 896 | function CanvasGradient_(aType) { 897 | this.type_ = aType; 898 | this.x0_ = 0; 899 | this.y0_ = 0; 900 | this.r0_ = 0; 901 | this.x1_ = 0; 902 | this.y1_ = 0; 903 | this.r1_ = 0; 904 | this.colors_ = []; 905 | } 906 | 907 | CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) { 908 | aColor = processStyle(aColor); 909 | this.colors_.push({offset: aOffset, 910 | color: aColor.color, 911 | alpha: aColor.alpha}); 912 | }; 913 | 914 | function CanvasPattern_() {} 915 | 916 | // set up externs 917 | G_vmlCanvasManager = G_vmlCanvasManager_; 918 | CanvasRenderingContext2D = CanvasRenderingContext2D_; 919 | CanvasGradient = CanvasGradient_; 920 | CanvasPattern = CanvasPattern_; 921 | 922 | })(); 923 | 924 | } // if 925 | -------------------------------------------------------------------------------- /public/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.2.2 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 27 | * ======================================================= */ 28 | 29 | $(function () { 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd otransitionend' 40 | , 'transition' : 'transitionend' 41 | } 42 | , name 43 | 44 | for (name in transEndEventNames){ 45 | if (el.style[name] !== undefined) { 46 | return transEndEventNames[name] 47 | } 48 | } 49 | 50 | }()) 51 | 52 | return transitionEnd && { 53 | end: transitionEnd 54 | } 55 | 56 | })() 57 | 58 | }) 59 | 60 | }(window.jQuery);/* ========================================================== 61 | * bootstrap-alert.js v2.2.2 62 | * http://twitter.github.com/bootstrap/javascript.html#alerts 63 | * ========================================================== 64 | * Copyright 2012 Twitter, Inc. 65 | * 66 | * Licensed under the Apache License, Version 2.0 (the "License"); 67 | * you may not use this file except in compliance with the License. 68 | * You may obtain a copy of the License at 69 | * 70 | * http://www.apache.org/licenses/LICENSE-2.0 71 | * 72 | * Unless required by applicable law or agreed to in writing, software 73 | * distributed under the License is distributed on an "AS IS" BASIS, 74 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 75 | * See the License for the specific language governing permissions and 76 | * limitations under the License. 77 | * ========================================================== */ 78 | 79 | 80 | !function ($) { 81 | 82 | "use strict"; // jshint ;_; 83 | 84 | 85 | /* ALERT CLASS DEFINITION 86 | * ====================== */ 87 | 88 | var dismiss = '[data-dismiss="alert"]' 89 | , Alert = function (el) { 90 | $(el).on('click', dismiss, this.close) 91 | } 92 | 93 | Alert.prototype.close = function (e) { 94 | var $this = $(this) 95 | , selector = $this.attr('data-target') 96 | , $parent 97 | 98 | if (!selector) { 99 | selector = $this.attr('href') 100 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 101 | } 102 | 103 | $parent = $(selector) 104 | 105 | e && e.preventDefault() 106 | 107 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 108 | 109 | $parent.trigger(e = $.Event('close')) 110 | 111 | if (e.isDefaultPrevented()) return 112 | 113 | $parent.removeClass('in') 114 | 115 | function removeElement() { 116 | $parent 117 | .trigger('closed') 118 | .remove() 119 | } 120 | 121 | $.support.transition && $parent.hasClass('fade') ? 122 | $parent.on($.support.transition.end, removeElement) : 123 | removeElement() 124 | } 125 | 126 | 127 | /* ALERT PLUGIN DEFINITION 128 | * ======================= */ 129 | 130 | var old = $.fn.alert 131 | 132 | $.fn.alert = function (option) { 133 | return this.each(function () { 134 | var $this = $(this) 135 | , data = $this.data('alert') 136 | if (!data) $this.data('alert', (data = new Alert(this))) 137 | if (typeof option == 'string') data[option].call($this) 138 | }) 139 | } 140 | 141 | $.fn.alert.Constructor = Alert 142 | 143 | 144 | /* ALERT NO CONFLICT 145 | * ================= */ 146 | 147 | $.fn.alert.noConflict = function () { 148 | $.fn.alert = old 149 | return this 150 | } 151 | 152 | 153 | /* ALERT DATA-API 154 | * ============== */ 155 | 156 | $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) 157 | 158 | }(window.jQuery);/* ============================================================ 159 | * bootstrap-button.js v2.2.2 160 | * http://twitter.github.com/bootstrap/javascript.html#buttons 161 | * ============================================================ 162 | * Copyright 2012 Twitter, Inc. 163 | * 164 | * Licensed under the Apache License, Version 2.0 (the "License"); 165 | * you may not use this file except in compliance with the License. 166 | * You may obtain a copy of the License at 167 | * 168 | * http://www.apache.org/licenses/LICENSE-2.0 169 | * 170 | * Unless required by applicable law or agreed to in writing, software 171 | * distributed under the License is distributed on an "AS IS" BASIS, 172 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 173 | * See the License for the specific language governing permissions and 174 | * limitations under the License. 175 | * ============================================================ */ 176 | 177 | 178 | !function ($) { 179 | 180 | "use strict"; // jshint ;_; 181 | 182 | 183 | /* BUTTON PUBLIC CLASS DEFINITION 184 | * ============================== */ 185 | 186 | var Button = function (element, options) { 187 | this.$element = $(element) 188 | this.options = $.extend({}, $.fn.button.defaults, options) 189 | } 190 | 191 | Button.prototype.setState = function (state) { 192 | var d = 'disabled' 193 | , $el = this.$element 194 | , data = $el.data() 195 | , val = $el.is('input') ? 'val' : 'html' 196 | 197 | state = state + 'Text' 198 | data.resetText || $el.data('resetText', $el[val]()) 199 | 200 | $el[val](data[state] || this.options[state]) 201 | 202 | // push to event loop to allow forms to submit 203 | setTimeout(function () { 204 | state == 'loadingText' ? 205 | $el.addClass(d).attr(d, d) : 206 | $el.removeClass(d).removeAttr(d) 207 | }, 0) 208 | } 209 | 210 | Button.prototype.toggle = function () { 211 | var $parent = this.$element.closest('[data-toggle="buttons-radio"]') 212 | 213 | $parent && $parent 214 | .find('.active') 215 | .removeClass('active') 216 | 217 | this.$element.toggleClass('active') 218 | } 219 | 220 | 221 | /* BUTTON PLUGIN DEFINITION 222 | * ======================== */ 223 | 224 | var old = $.fn.button 225 | 226 | $.fn.button = function (option) { 227 | return this.each(function () { 228 | var $this = $(this) 229 | , data = $this.data('button') 230 | , options = typeof option == 'object' && option 231 | if (!data) $this.data('button', (data = new Button(this, options))) 232 | if (option == 'toggle') data.toggle() 233 | else if (option) data.setState(option) 234 | }) 235 | } 236 | 237 | $.fn.button.defaults = { 238 | loadingText: 'loading...' 239 | } 240 | 241 | $.fn.button.Constructor = Button 242 | 243 | 244 | /* BUTTON NO CONFLICT 245 | * ================== */ 246 | 247 | $.fn.button.noConflict = function () { 248 | $.fn.button = old 249 | return this 250 | } 251 | 252 | 253 | /* BUTTON DATA-API 254 | * =============== */ 255 | 256 | $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { 257 | var $btn = $(e.target) 258 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 259 | $btn.button('toggle') 260 | }) 261 | 262 | }(window.jQuery);/* ========================================================== 263 | * bootstrap-carousel.js v2.2.2 264 | * http://twitter.github.com/bootstrap/javascript.html#carousel 265 | * ========================================================== 266 | * Copyright 2012 Twitter, Inc. 267 | * 268 | * Licensed under the Apache License, Version 2.0 (the "License"); 269 | * you may not use this file except in compliance with the License. 270 | * You may obtain a copy of the License at 271 | * 272 | * http://www.apache.org/licenses/LICENSE-2.0 273 | * 274 | * Unless required by applicable law or agreed to in writing, software 275 | * distributed under the License is distributed on an "AS IS" BASIS, 276 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 277 | * See the License for the specific language governing permissions and 278 | * limitations under the License. 279 | * ========================================================== */ 280 | 281 | 282 | !function ($) { 283 | 284 | "use strict"; // jshint ;_; 285 | 286 | 287 | /* CAROUSEL CLASS DEFINITION 288 | * ========================= */ 289 | 290 | var Carousel = function (element, options) { 291 | this.$element = $(element) 292 | this.options = options 293 | this.options.pause == 'hover' && this.$element 294 | .on('mouseenter', $.proxy(this.pause, this)) 295 | .on('mouseleave', $.proxy(this.cycle, this)) 296 | } 297 | 298 | Carousel.prototype = { 299 | 300 | cycle: function (e) { 301 | if (!e) this.paused = false 302 | this.options.interval 303 | && !this.paused 304 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 305 | return this 306 | } 307 | 308 | , to: function (pos) { 309 | var $active = this.$element.find('.item.active') 310 | , children = $active.parent().children() 311 | , activePos = children.index($active) 312 | , that = this 313 | 314 | if (pos > (children.length - 1) || pos < 0) return 315 | 316 | if (this.sliding) { 317 | return this.$element.one('slid', function () { 318 | that.to(pos) 319 | }) 320 | } 321 | 322 | if (activePos == pos) { 323 | return this.pause().cycle() 324 | } 325 | 326 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) 327 | } 328 | 329 | , pause: function (e) { 330 | if (!e) this.paused = true 331 | if (this.$element.find('.next, .prev').length && $.support.transition.end) { 332 | this.$element.trigger($.support.transition.end) 333 | this.cycle() 334 | } 335 | clearInterval(this.interval) 336 | this.interval = null 337 | return this 338 | } 339 | 340 | , next: function () { 341 | if (this.sliding) return 342 | return this.slide('next') 343 | } 344 | 345 | , prev: function () { 346 | if (this.sliding) return 347 | return this.slide('prev') 348 | } 349 | 350 | , slide: function (type, next) { 351 | var $active = this.$element.find('.item.active') 352 | , $next = next || $active[type]() 353 | , isCycling = this.interval 354 | , direction = type == 'next' ? 'left' : 'right' 355 | , fallback = type == 'next' ? 'first' : 'last' 356 | , that = this 357 | , e 358 | 359 | this.sliding = true 360 | 361 | isCycling && this.pause() 362 | 363 | $next = $next.length ? $next : this.$element.find('.item')[fallback]() 364 | 365 | e = $.Event('slide', { 366 | relatedTarget: $next[0] 367 | }) 368 | 369 | if ($next.hasClass('active')) return 370 | 371 | if ($.support.transition && this.$element.hasClass('slide')) { 372 | this.$element.trigger(e) 373 | if (e.isDefaultPrevented()) return 374 | $next.addClass(type) 375 | $next[0].offsetWidth // force reflow 376 | $active.addClass(direction) 377 | $next.addClass(direction) 378 | this.$element.one($.support.transition.end, function () { 379 | $next.removeClass([type, direction].join(' ')).addClass('active') 380 | $active.removeClass(['active', direction].join(' ')) 381 | that.sliding = false 382 | setTimeout(function () { that.$element.trigger('slid') }, 0) 383 | }) 384 | } else { 385 | this.$element.trigger(e) 386 | if (e.isDefaultPrevented()) return 387 | $active.removeClass('active') 388 | $next.addClass('active') 389 | this.sliding = false 390 | this.$element.trigger('slid') 391 | } 392 | 393 | isCycling && this.cycle() 394 | 395 | return this 396 | } 397 | 398 | } 399 | 400 | 401 | /* CAROUSEL PLUGIN DEFINITION 402 | * ========================== */ 403 | 404 | var old = $.fn.carousel 405 | 406 | $.fn.carousel = function (option) { 407 | return this.each(function () { 408 | var $this = $(this) 409 | , data = $this.data('carousel') 410 | , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) 411 | , action = typeof option == 'string' ? option : options.slide 412 | if (!data) $this.data('carousel', (data = new Carousel(this, options))) 413 | if (typeof option == 'number') data.to(option) 414 | else if (action) data[action]() 415 | else if (options.interval) data.cycle() 416 | }) 417 | } 418 | 419 | $.fn.carousel.defaults = { 420 | interval: 5000 421 | , pause: 'hover' 422 | } 423 | 424 | $.fn.carousel.Constructor = Carousel 425 | 426 | 427 | /* CAROUSEL NO CONFLICT 428 | * ==================== */ 429 | 430 | $.fn.carousel.noConflict = function () { 431 | $.fn.carousel = old 432 | return this 433 | } 434 | 435 | /* CAROUSEL DATA-API 436 | * ================= */ 437 | 438 | $(document).on('click.carousel.data-api', '[data-slide]', function (e) { 439 | var $this = $(this), href 440 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 441 | , options = $.extend({}, $target.data(), $this.data()) 442 | $target.carousel(options) 443 | e.preventDefault() 444 | }) 445 | 446 | }(window.jQuery);/* ============================================================= 447 | * bootstrap-collapse.js v2.2.2 448 | * http://twitter.github.com/bootstrap/javascript.html#collapse 449 | * ============================================================= 450 | * Copyright 2012 Twitter, Inc. 451 | * 452 | * Licensed under the Apache License, Version 2.0 (the "License"); 453 | * you may not use this file except in compliance with the License. 454 | * You may obtain a copy of the License at 455 | * 456 | * http://www.apache.org/licenses/LICENSE-2.0 457 | * 458 | * Unless required by applicable law or agreed to in writing, software 459 | * distributed under the License is distributed on an "AS IS" BASIS, 460 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 461 | * See the License for the specific language governing permissions and 462 | * limitations under the License. 463 | * ============================================================ */ 464 | 465 | 466 | !function ($) { 467 | 468 | "use strict"; // jshint ;_; 469 | 470 | 471 | /* COLLAPSE PUBLIC CLASS DEFINITION 472 | * ================================ */ 473 | 474 | var Collapse = function (element, options) { 475 | this.$element = $(element) 476 | this.options = $.extend({}, $.fn.collapse.defaults, options) 477 | 478 | if (this.options.parent) { 479 | this.$parent = $(this.options.parent) 480 | } 481 | 482 | this.options.toggle && this.toggle() 483 | } 484 | 485 | Collapse.prototype = { 486 | 487 | constructor: Collapse 488 | 489 | , dimension: function () { 490 | var hasWidth = this.$element.hasClass('width') 491 | return hasWidth ? 'width' : 'height' 492 | } 493 | 494 | , show: function () { 495 | var dimension 496 | , scroll 497 | , actives 498 | , hasData 499 | 500 | if (this.transitioning) return 501 | 502 | dimension = this.dimension() 503 | scroll = $.camelCase(['scroll', dimension].join('-')) 504 | actives = this.$parent && this.$parent.find('> .accordion-group > .in') 505 | 506 | if (actives && actives.length) { 507 | hasData = actives.data('collapse') 508 | if (hasData && hasData.transitioning) return 509 | actives.collapse('hide') 510 | hasData || actives.data('collapse', null) 511 | } 512 | 513 | this.$element[dimension](0) 514 | this.transition('addClass', $.Event('show'), 'shown') 515 | $.support.transition && this.$element[dimension](this.$element[0][scroll]) 516 | } 517 | 518 | , hide: function () { 519 | var dimension 520 | if (this.transitioning) return 521 | dimension = this.dimension() 522 | this.reset(this.$element[dimension]()) 523 | this.transition('removeClass', $.Event('hide'), 'hidden') 524 | this.$element[dimension](0) 525 | } 526 | 527 | , reset: function (size) { 528 | var dimension = this.dimension() 529 | 530 | this.$element 531 | .removeClass('collapse') 532 | [dimension](size || 'auto') 533 | [0].offsetWidth 534 | 535 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') 536 | 537 | return this 538 | } 539 | 540 | , transition: function (method, startEvent, completeEvent) { 541 | var that = this 542 | , complete = function () { 543 | if (startEvent.type == 'show') that.reset() 544 | that.transitioning = 0 545 | that.$element.trigger(completeEvent) 546 | } 547 | 548 | this.$element.trigger(startEvent) 549 | 550 | if (startEvent.isDefaultPrevented()) return 551 | 552 | this.transitioning = 1 553 | 554 | this.$element[method]('in') 555 | 556 | $.support.transition && this.$element.hasClass('collapse') ? 557 | this.$element.one($.support.transition.end, complete) : 558 | complete() 559 | } 560 | 561 | , toggle: function () { 562 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 563 | } 564 | 565 | } 566 | 567 | 568 | /* COLLAPSE PLUGIN DEFINITION 569 | * ========================== */ 570 | 571 | var old = $.fn.collapse 572 | 573 | $.fn.collapse = function (option) { 574 | return this.each(function () { 575 | var $this = $(this) 576 | , data = $this.data('collapse') 577 | , options = typeof option == 'object' && option 578 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 579 | if (typeof option == 'string') data[option]() 580 | }) 581 | } 582 | 583 | $.fn.collapse.defaults = { 584 | toggle: true 585 | } 586 | 587 | $.fn.collapse.Constructor = Collapse 588 | 589 | 590 | /* COLLAPSE NO CONFLICT 591 | * ==================== */ 592 | 593 | $.fn.collapse.noConflict = function () { 594 | $.fn.collapse = old 595 | return this 596 | } 597 | 598 | 599 | /* COLLAPSE DATA-API 600 | * ================= */ 601 | 602 | $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { 603 | var $this = $(this), href 604 | , target = $this.attr('data-target') 605 | || e.preventDefault() 606 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 607 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 608 | $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') 609 | $(target).collapse(option) 610 | }) 611 | 612 | }(window.jQuery);/* ============================================================ 613 | * bootstrap-dropdown.js v2.2.2 614 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 615 | * ============================================================ 616 | * Copyright 2012 Twitter, Inc. 617 | * 618 | * Licensed under the Apache License, Version 2.0 (the "License"); 619 | * you may not use this file except in compliance with the License. 620 | * You may obtain a copy of the License at 621 | * 622 | * http://www.apache.org/licenses/LICENSE-2.0 623 | * 624 | * Unless required by applicable law or agreed to in writing, software 625 | * distributed under the License is distributed on an "AS IS" BASIS, 626 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 627 | * See the License for the specific language governing permissions and 628 | * limitations under the License. 629 | * ============================================================ */ 630 | 631 | 632 | !function ($) { 633 | 634 | "use strict"; // jshint ;_; 635 | 636 | 637 | /* DROPDOWN CLASS DEFINITION 638 | * ========================= */ 639 | 640 | var toggle = '[data-toggle=dropdown]' 641 | , Dropdown = function (element) { 642 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 643 | $('html').on('click.dropdown.data-api', function () { 644 | $el.parent().removeClass('open') 645 | }) 646 | } 647 | 648 | Dropdown.prototype = { 649 | 650 | constructor: Dropdown 651 | 652 | , toggle: function (e) { 653 | var $this = $(this) 654 | , $parent 655 | , isActive 656 | 657 | if ($this.is('.disabled, :disabled')) return 658 | 659 | $parent = getParent($this) 660 | 661 | isActive = $parent.hasClass('open') 662 | 663 | clearMenus() 664 | 665 | if (!isActive) { 666 | $parent.toggleClass('open') 667 | } 668 | 669 | $this.focus() 670 | 671 | return false 672 | } 673 | 674 | , keydown: function (e) { 675 | var $this 676 | , $items 677 | , $active 678 | , $parent 679 | , isActive 680 | , index 681 | 682 | if (!/(38|40|27)/.test(e.keyCode)) return 683 | 684 | $this = $(this) 685 | 686 | e.preventDefault() 687 | e.stopPropagation() 688 | 689 | if ($this.is('.disabled, :disabled')) return 690 | 691 | $parent = getParent($this) 692 | 693 | isActive = $parent.hasClass('open') 694 | 695 | if (!isActive || (isActive && e.keyCode == 27)) return $this.click() 696 | 697 | $items = $('[role=menu] li:not(.divider):visible a', $parent) 698 | 699 | if (!$items.length) return 700 | 701 | index = $items.index($items.filter(':focus')) 702 | 703 | if (e.keyCode == 38 && index > 0) index-- // up 704 | if (e.keyCode == 40 && index < $items.length - 1) index++ // down 705 | if (!~index) index = 0 706 | 707 | $items 708 | .eq(index) 709 | .focus() 710 | } 711 | 712 | } 713 | 714 | function clearMenus() { 715 | $(toggle).each(function () { 716 | getParent($(this)).removeClass('open') 717 | }) 718 | } 719 | 720 | function getParent($this) { 721 | var selector = $this.attr('data-target') 722 | , $parent 723 | 724 | if (!selector) { 725 | selector = $this.attr('href') 726 | selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 727 | } 728 | 729 | $parent = $(selector) 730 | $parent.length || ($parent = $this.parent()) 731 | 732 | return $parent 733 | } 734 | 735 | 736 | /* DROPDOWN PLUGIN DEFINITION 737 | * ========================== */ 738 | 739 | var old = $.fn.dropdown 740 | 741 | $.fn.dropdown = function (option) { 742 | return this.each(function () { 743 | var $this = $(this) 744 | , data = $this.data('dropdown') 745 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 746 | if (typeof option == 'string') data[option].call($this) 747 | }) 748 | } 749 | 750 | $.fn.dropdown.Constructor = Dropdown 751 | 752 | 753 | /* DROPDOWN NO CONFLICT 754 | * ==================== */ 755 | 756 | $.fn.dropdown.noConflict = function () { 757 | $.fn.dropdown = old 758 | return this 759 | } 760 | 761 | 762 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 763 | * =================================== */ 764 | 765 | $(document) 766 | .on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus) 767 | .on('click.dropdown touchstart.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) 768 | .on('touchstart.dropdown.data-api', '.dropdown-menu', function (e) { e.stopPropagation() }) 769 | .on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle) 770 | .on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown) 771 | 772 | }(window.jQuery);/* ========================================================= 773 | * bootstrap-modal.js v2.2.2 774 | * http://twitter.github.com/bootstrap/javascript.html#modals 775 | * ========================================================= 776 | * Copyright 2012 Twitter, Inc. 777 | * 778 | * Licensed under the Apache License, Version 2.0 (the "License"); 779 | * you may not use this file except in compliance with the License. 780 | * You may obtain a copy of the License at 781 | * 782 | * http://www.apache.org/licenses/LICENSE-2.0 783 | * 784 | * Unless required by applicable law or agreed to in writing, software 785 | * distributed under the License is distributed on an "AS IS" BASIS, 786 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 787 | * See the License for the specific language governing permissions and 788 | * limitations under the License. 789 | * ========================================================= */ 790 | 791 | 792 | !function ($) { 793 | 794 | "use strict"; // jshint ;_; 795 | 796 | 797 | /* MODAL CLASS DEFINITION 798 | * ====================== */ 799 | 800 | var Modal = function (element, options) { 801 | this.options = options 802 | this.$element = $(element) 803 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 804 | this.options.remote && this.$element.find('.modal-body').load(this.options.remote) 805 | } 806 | 807 | Modal.prototype = { 808 | 809 | constructor: Modal 810 | 811 | , toggle: function () { 812 | return this[!this.isShown ? 'show' : 'hide']() 813 | } 814 | 815 | , show: function () { 816 | var that = this 817 | , e = $.Event('show') 818 | 819 | this.$element.trigger(e) 820 | 821 | if (this.isShown || e.isDefaultPrevented()) return 822 | 823 | this.isShown = true 824 | 825 | this.escape() 826 | 827 | this.backdrop(function () { 828 | var transition = $.support.transition && that.$element.hasClass('fade') 829 | 830 | if (!that.$element.parent().length) { 831 | that.$element.appendTo(document.body) //don't move modals dom position 832 | } 833 | 834 | that.$element 835 | .show() 836 | 837 | if (transition) { 838 | that.$element[0].offsetWidth // force reflow 839 | } 840 | 841 | that.$element 842 | .addClass('in') 843 | .attr('aria-hidden', false) 844 | 845 | that.enforceFocus() 846 | 847 | transition ? 848 | that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) : 849 | that.$element.focus().trigger('shown') 850 | 851 | }) 852 | } 853 | 854 | , hide: function (e) { 855 | e && e.preventDefault() 856 | 857 | var that = this 858 | 859 | e = $.Event('hide') 860 | 861 | this.$element.trigger(e) 862 | 863 | if (!this.isShown || e.isDefaultPrevented()) return 864 | 865 | this.isShown = false 866 | 867 | this.escape() 868 | 869 | $(document).off('focusin.modal') 870 | 871 | this.$element 872 | .removeClass('in') 873 | .attr('aria-hidden', true) 874 | 875 | $.support.transition && this.$element.hasClass('fade') ? 876 | this.hideWithTransition() : 877 | this.hideModal() 878 | } 879 | 880 | , enforceFocus: function () { 881 | var that = this 882 | $(document).on('focusin.modal', function (e) { 883 | if (that.$element[0] !== e.target && !that.$element.has(e.target).length) { 884 | that.$element.focus() 885 | } 886 | }) 887 | } 888 | 889 | , escape: function () { 890 | var that = this 891 | if (this.isShown && this.options.keyboard) { 892 | this.$element.on('keyup.dismiss.modal', function ( e ) { 893 | e.which == 27 && that.hide() 894 | }) 895 | } else if (!this.isShown) { 896 | this.$element.off('keyup.dismiss.modal') 897 | } 898 | } 899 | 900 | , hideWithTransition: function () { 901 | var that = this 902 | , timeout = setTimeout(function () { 903 | that.$element.off($.support.transition.end) 904 | that.hideModal() 905 | }, 500) 906 | 907 | this.$element.one($.support.transition.end, function () { 908 | clearTimeout(timeout) 909 | that.hideModal() 910 | }) 911 | } 912 | 913 | , hideModal: function (that) { 914 | this.$element 915 | .hide() 916 | .trigger('hidden') 917 | 918 | this.backdrop() 919 | } 920 | 921 | , removeBackdrop: function () { 922 | this.$backdrop.remove() 923 | this.$backdrop = null 924 | } 925 | 926 | , backdrop: function (callback) { 927 | var that = this 928 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 929 | 930 | if (this.isShown && this.options.backdrop) { 931 | var doAnimate = $.support.transition && animate 932 | 933 | this.$backdrop = $('