diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 92a3816..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,97 +0,0 @@ -module.exports = function(grunt) { - - "use strict"; - - // Initializes the Grunt tasks with the following settings - grunt.initConfig({ - - // A list of files which will be syntax-checked by JSHint. - jshint: { - files: ['src/js/shims.js', 'src/js/firechat.js', 'src/js/firechat-ui.js'], - options: { - regexdash: false - } - }, - - // Precompile templates and strip whitespace with 'processContent'. - jst: { - compile: { - options: { - path: 'templates', - namespace: 'FirechatDefaultTemplates', - prettify: true, - processContent: function(src) { - return src.replace(/(^\s+|\s+$)/gm, ''); - } - }, - files: { - 'compiled/templates.js': ['templates/*.html'] - } - } - }, - - // Compile and minify LESS CSS for production. - less: { - development: { - files: { - "build/firechat-default.css": "src/less/styles.less" - } - }, - production: { - options: { - yuicompress: true - }, - files: { - "build/firechat-default.min.css": "src/less/styles.less" - } - } - }, - - // Concatenate files in a specific order. - concat: { - js: { - src: [ - 'src/js/libs/underscore-1.4.4.min.js', - 'compiled/templates.js', - 'src/js/shims.js', - 'src/js/firechat.js', - 'src/js/firechat-ui.js' - ], - dest: 'build/firechat-default.js' - } - }, - - // Minify concatenated files. - uglify: { - dist: { - src: ['<%= concat.js.dest %>'], - dest: 'build/firechat-default.min.js' - } - }, - - // Clean up temporary files. - clean: ['compiled/'], - - // Tasks to execute upon file change when using `grunt watch`. - watch: { - src: { - files: ['src/**/*.*', 'templates/**/*.*'], - tasks: ['default'] - } - } - }); - - // Load specific plugins, which have been installed and specified in package.json. - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-jst'); - grunt.loadNpmTasks('grunt-contrib-less'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-docco'); - - // Default task operations if simply calling `grunt` without options. - grunt.registerTask('default', ['jshint', 'jst', 'less', 'concat', 'uglify', 'clean']); - -}; diff --git a/README.md b/README.md index 94bd835..4e836fd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -FireChat.js +Firechat.js =========== -Simple, extensible chat build on [Firebase](https://firebase.com). +Simple, extensible chat built on [Firebase](https://firebase.com). -For demos, documentation, and integration instructions, see [http://firebase.github.io/firechat/](http://firebase.github.io/firechat/). \ No newline at end of file +For demos, documentation, and integration instructions, see [http://firebase.github.io/firechat/](http://firebase.github.io/firechat). diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..b30ad58 --- /dev/null +++ b/_config.yml @@ -0,0 +1,4 @@ +safe: true +lsi: false +pygments: true +markdown: rdiscount diff --git a/_layouts/docs.html b/_layouts/docs.html new file mode 100644 index 0000000..8c8a862 --- /dev/null +++ b/_layouts/docs.html @@ -0,0 +1,96 @@ + + + + + + Firechat - Documentation + + + + + + + + + + + + + + Fork me on GitHub + + +
+
+
+ +
+
+ +
+
+ +
+
+ {{ content }} +
Build something cool using Firechat? We'd + love to hear from you! +
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/assets/img/icons.png b/assets/img/icons.png deleted file mode 100644 index 79bec78..0000000 Binary files a/assets/img/icons.png and /dev/null differ diff --git a/css/pygments-borland.css b/css/pygments-borland.css new file mode 100644 index 0000000..b4999e7 --- /dev/null +++ b/css/pygments-borland.css @@ -0,0 +1,46 @@ +.hll { background-color: #ffffcc } +.c { color: #008800; font-style: italic } /* Comment */ +.err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.k { color: #000080; font-weight: bold } /* Keyword */ +.cm { color: #008800; font-style: italic } /* Comment.Multiline */ +.cp { color: #008080 } /* Comment.Preproc */ +.c1 { color: #008800; font-style: italic } /* Comment.Single */ +.cs { color: #008800; font-weight: bold } /* Comment.Special */ +.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #aa0000 } /* Generic.Error */ +.gh { color: #999999 } /* Generic.Heading */ +.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.go { color: #888888 } /* Generic.Output */ +.gp { color: #555555 } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #aaaaaa } /* Generic.Subheading */ +.gt { color: #aa0000 } /* Generic.Traceback */ +.kc { color: #000080; font-weight: bold } /* Keyword.Constant */ +.kd { color: #000080; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #000080; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #000080; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #000080; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #000080; font-weight: bold } /* Keyword.Type */ +.m { color: #0000FF } /* Literal.Number */ +.s { color: #0000FF } /* Literal.String */ +.na { color: #FF0000 } /* Name.Attribute */ +.nt { color: #000080; font-weight: bold } /* Name.Tag */ +.ow { font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #0000FF } /* Literal.Number.Float */ +.mh { color: #0000FF } /* Literal.Number.Hex */ +.mi { color: #0000FF } /* Literal.Number.Integer */ +.mo { color: #0000FF } /* Literal.Number.Oct */ +.sb { color: #0000FF } /* Literal.String.Backtick */ +.sc { color: #800080 } /* Literal.String.Char */ +.sd { color: #0000FF } /* Literal.String.Doc */ +.s2 { color: #0000FF } /* Literal.String.Double */ +.se { color: #0000FF } /* Literal.String.Escape */ +.sh { color: #0000FF } /* Literal.String.Heredoc */ +.si { color: #0000FF } /* Literal.String.Interpol */ +.sx { color: #0000FF } /* Literal.String.Other */ +.sr { color: #0000FF } /* Literal.String.Regex */ +.s1 { color: #0000FF } /* Literal.String.Single */ +.ss { color: #0000FF } /* Literal.String.Symbol */ +.il { color: #0000FF } /* Literal.Number.Integer.Long */ diff --git a/css/styles.css b/css/styles.css new file mode 100644 index 0000000..5d5b800 --- /dev/null +++ b/css/styles.css @@ -0,0 +1,565 @@ +@import url(http://fonts.googleapis.com/css?family=Satisfy|Lato:300,700,300italic,700italic); + +/* Global +-------------------------------------------------- */ +body { + font: 14.5px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; + color:#777; + font-weight:300; +} + +a { + color: #08c; +} + +a:hover { + text-decoration: underline; + color: #004f77; +} + +img.fork-on-github { + position: absolute; + top: 0; + left: 0; + border: 0; + width: 149px; + height: 149px; + z-index: 1; +} + +.strong { font-weight: bold; } + +.satisfy { font-family: 'Satisfy', cursive; } + +/* Global: Header +-------------------------------------------------- */ +header { + position: relative; + min-height: 150px; + width: 100%; + color: white; + background-color: #ff8b00; +} + +.header-title { + background-color: #d7690d; +} + +.header-content { + background-color: #ff8b00; +} + +.header-links { + padding-top: 15px; + text-align: right; + font-size: 15px; +} + +.header-links a { + display: inline-block; + margin: 0 15px; + text-decoration: none; + color: #fff; +} + +.header-links a:hover { + text-decoration: underline; + color: #feffff; +} + +.header-links a.selected { + text-decoration: underline; + /* color: #fff; */ + color: #feffff; + cursor: default; +} + +#download-on-github { + float: right; + font-size: 24px; + padding: 10px 40px; + margin: 35px 0; + border-bottom: 6px solid #ccc; +} + +#top-shadow { + position: absolute; + width: 100%; + bottom: 0; + height: 60px; + background: url('../images/top-shadow.png') no-repeat center; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +#title-small { + color: #fff; + position: absolute; + left: 150px; + top: 15px; + width: 126px; + height: 42px; + font-size: 34px; +} + +#page-title { + position: absolute; + left: 150px; + top: 60px; + font-size: 60px; + font-weight: bold; + line-height: 1.2em; +} + +#page-links { + position: absolute; + left: 90px; + top: 155px; + font-size: 18px; +} + +#page-links a { + color: #fff; +} + +/* Firechat Demo +-------------------------------------------------- */ +#firechat-container { + height: 475px; + max-width: 325px; + padding: 10px; + border: 1px solid #ccc; + background-color: #fff; + margin: auto auto; + text-align: center; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 5px 25px #666; + -moz-box-shadow: 0 5px 25px #666; + box-shadow: 0 5px 25px #666; +} + +#auth-modal { + width: 30%; + margin-left: -15%; + left: 50%; +} + +#user-info { + display: block; + margin-top: 10px; +} + +#user-info a { + color: #fff; + text-decoration: underline; +} + +/* Social Buttons +-------------------------------------------------- */ +.social-container { + padding: 10px 0; + color: #5e5e5e; + text-align: center; + border-bottom: 1px solid #e0e0e0; + background-color: #f7f7f7; +} +.social-buttons { + margin-top: 2px; + margin-left: 0; + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.social-buttons li { + display: inline-block; + padding: 6px 8px 5px; + line-height: 1; + *display: inline; + *zoom: 1; +} + +/************************************************/ +/* Stuff below-the-fold (the white part) */ +/************************************************/ +#bottom-container { + border: 1px solid #e2e2e2; + border-top: 0; + background-color: white; + margin: 0 auto; + z-index: 1; + -webkit-box-shadow: 0 0 6px 1px #bbb; + -moz-box-shadow: 0 0 6px 1px #bbb; + box-shadow: 0 0 6px 1px #bbb; +} + +.bottom-content { + padding: 30px 0px 0; + color: #5e5e5e; + margin-bottom: 40px; +} + +.bottom-content.separator { + margin-top: 40px; + border-top: 1px solid #e0e0e0; +} + +.build-something { + margin: auto; + margin-top: 30px; + width: 500px; + font-style: italic; +} + +/* Footer +-------------------------------------------------- */ +footer { + border-top: 1px solid #ccc; + background-color: #f4f4f4; + margin-top: 50px; + height: 200px; + margin: 0 auto; + position: relative; +} + +#footer-links { + padding-top: 24px; + text-align: center; + margin-bottom: 20px; +} + +#powered-by-firebase { + display: inline-block; + height: 34px; + width: 129px; + background: url('../images/powered-by-firebase-dim.png') no-repeat center; +} + +#powered-by-firebase:hover { + background: url('../images/powered-by-firebase.png') no-repeat center; +} + +#footer-links a { + font-size: 14px; + color: #999; + text-decoration: none; + margin: 0 20px; + display: inline-block; +} + +#footer-links a:hover { + text-decoration: underline; + color: #333; +} + + +/* index.html +-------------------------------------------------- */ +.home-page { + background-color: #f4f4f4; +} + +.home-page #top-content { + height: 925px; + padding-bottom: 35px; +} + +.home-page footer { + border-top: none; +} + +#home-title { + margin: 45px auto -15px auto; + font-size: 72px; + text-align: center; +} + +#home-subtitle { + font-size: 36px; + text-align: center; + margin-bottom: 60px; + font-weight: 100; +} + +#home-download-on-github { + font-size: 24px; + padding: 25px 55px 19px; + margin: -40px auto 35px; + border-bottom: 6px solid #ccc; +} + +#code-container { + width: 600px; + margin: 22px auto; +} + +#code-heading { + text-align: left; + font-size: 22px; + margin-bottom: 2px; +} + +#code-heading a { + color: #f9e701; + text-decoration: underline; +} + +#code-container pre { + background-color: rgba(255, 255, 255, 0.3); + border: 1px solid rgba(0, 0, 0, 0.3); + margin: 0; + padding: 20px; + text-align: left; + font-size: 14px; + color: #000; + line-height: 20px; + font-family: monospace; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +#code-examples-link { + display: block; + float: right; + margin-top: 5px; + font-size: 17px; + color: #fff; +} + +.bottom-title { + font-size: 42px; + color: #333; + text-align: center; + padding: 30px 0 0; +} + +/* Docs +-------------------------------------------------- */ +.docs-page { + background: white; +} +.docs-content { + color: #5e5e5e; + margin-bottom: 40px; +} +.docs-page pre { + margin: 20px 10px; + padding: 15px 10px; +} +.docs-separator { + height: 0; + margin-top: 40px; + margin-bottom: 30px; + border-top: 1px solid #e0e0e0; +} +.docs-content p { + margin: 0 0 10px; +} + +.docs-content h1 { + margin: 20px 0 10px; +} +.docs-content h2 { + margin: 20px 0 10px; +} +.docs-content h3 { + margin: 20px 0 10px; +} +.docs-content h4 { + margin: 20px 0 10px; +} +.docs-content a { + color: #eb8717; +} +.docs-content .emphasis-box { + border: 1px solid #e5a165; + background: #ffbf86; + padding: 10px 90px; + color: #000; + font-size: 16px; +} +.docs-content .emphasis-box a { + color: #dc4700; +} +.error-page #top { + height: 200px; +} + +/* Docs: Annotated Source +-------------------------------------------------- */ +.docs-annotated-source-page iframe { + width: 100%; + min-height: 100%; + border: none; +} + +/* Sidenav for Docs +-------------------------------------------------- */ +.sidenav { + width: 220px; + margin: 35px 0 0; + padding: 0; + background-color: #fcfcfc; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 1px 4px rgba(0,0,0,.065); + -moz-box-shadow: 0 1px 4px rgba(0,0,0,.065); + box-shadow: 0 1px 4px rgba(0,0,0,.065); +} +.sidenav > li > a { + display: block; + width: 190px \9; + margin: 0 0 -1px; + padding: 8px 14px; + border: 1px solid #e5e5e5; +} +.sidenav > li:first-child > a { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.sidenav > li:last-child > a { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.sidenav > .active > a { + position: relative; + z-index: 2; + padding: 9px 15px; + border: 0; + text-shadow: 0 1px 0 rgba(0,0,0,.15); + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.1); + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.1); + box-shadow: inset 1px 0 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.1); +} +.sidenav .icon-chevron-right { + float: right; + margin-top: 2px; + margin-right: -6px; + opacity: .25; +} +.sidenav > li > a:hover { + background-color: #f5f5f5; +} +.sidenav a:hover .icon-chevron-right { + opacity: .5; +} +.sidenav .active .icon-chevron-right, +.sidenav .active a:hover .icon-chevron-right { + background-image: url(//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.css); + opacity: 1; +} +.sidenav.affix { + top: 40px; +} +.sidenav.affix-bottom { + position: absolute; + top: auto; + bottom: 270px; +} + + +/* Responsive +-------------------------------------------------- */ + +/* Desktop large +------------------------- */ +@media (min-width: 1200px) { + .sidenav { + width: 258px; + } + .sidenav > li > a { + width: 230px \9; + } +} + +/* Desktop +------------------------- */ +@media (max-width: 980px) { + .sidenav { + top: 0; + width: 218px; + margin-right: 0; + } +} + +/* Tablet to desktop +------------------------- */ +@media (min-width: 768px) and (max-width: 979px) { + .home-page #top-content { + height: 925px; + } + .sidenav { + width: 166px; + } + .sidenav.affix { + top: 0; + } +} + +/* Tablet +------------------------- */ +@media (min-width: 481px) and (max-width: 767px) { + body { + padding: 0; + } + .home-page #top-content { + height: 975px; + } + .sidenav { + width: auto; + margin-bottom: 20px; + } + .sidenav.affix { + position: static; + width: auto; + top: 0; + } + #docs-container { + padding: 0 15px; + } + .bottom-content { + padding: 0 15px; + } +} + +/* Landscape phones +------------------------- */ +@media (max-width: 480px) { + body { + padding: 0; + } + .home-page #top-content { + height: 985px; + } + .header-links { + text-align: center; + } + img.fork-on-github { + display: none; + } + .sidenav { + width: auto; + margin-bottom: 20px; + } + .sidenav.affix { + position: static; + width: auto; + top: 0; + } + #docs-container { + padding: 0 15px; + } + .social-buttons li { + padding: 6px 0px 5px; + } + .bottom-content { + padding: 0 15px; + } +} diff --git a/docs/annotated-source.html b/docs/annotated-source.html new file mode 100644 index 0000000..7b2e482 --- /dev/null +++ b/docs/annotated-source.html @@ -0,0 +1,83 @@ + + + + + + Firechat - Documentation + + + + + + + + + + + + + + Fork me on GitHub + + +
+
+
+ +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/docs/docco.css b/docs/docco.css new file mode 100644 index 0000000..f690a07 --- /dev/null +++ b/docs/docco.css @@ -0,0 +1,500 @@ +/*--------------------- Typography ----------------------------*/ + +@font-face { + font-family: 'aller-light'; + src: url('public/fonts/aller-light.eot'); + src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'), + url('public/fonts/aller-light.woff') format('woff'), + url('public/fonts/aller-light.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'aller-bold'; + src: url('public/fonts/aller-bold.eot'); + src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'), + url('public/fonts/aller-bold.woff') format('woff'), + url('public/fonts/aller-bold.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'novecento-bold'; + src: url('public/fonts/novecento-bold.eot'); + src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'), + url('public/fonts/novecento-bold.woff') format('woff'), + url('public/fonts/novecento-bold.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +/*--------------------- Layout ----------------------------*/ +html { height: 100%; } +body { + font-family: "aller-light"; + font-size: 14px; + line-height: 18px; + color: #30404f; + margin: 0; padding: 0; + height:100%; +} +#container { min-height: 100%; } + +a { + color: #000; +} + +b, strong { + font-weight: normal; + font-family: "aller-bold"; +} + +p, ul, ol { + margin: 15px 0 0px; +} + +h1, h2, h3, h4, h5, h6 { + color: #112233; + line-height: 1em; + font-weight: normal; + font-family: "novecento-bold"; + text-transform: uppercase; + margin: 30px 0 15px 0; +} + +h1 { + margin-top: 40px; +} + +hr { + border: 0; + background: 1px solid #ddd; + height: 1px; + margin: 20px 0; +} + +pre, tt, code { + font-size: 12px; line-height: 16px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; +} + .annotation pre { + display: block; + margin: 0; + padding: 7px 10px; + background: #fcfcfc; + -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); + -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); + box-shadow: inset 0 0 10px rgba(0,0,0,0.1); + overflow-x: auto; + } + .annotation pre code { + border: 0; + padding: 0; + background: transparent; + } + + +blockquote { + border-left: 5px solid #ccc; + margin: 0; + padding: 1px 0 1px 1em; +} + .sections blockquote p { + font-family: Menlo, Consolas, Monaco, monospace; + font-size: 12px; line-height: 16px; + color: #999; + margin: 10px 0 0; + white-space: pre-wrap; + } + +ul.sections { + list-style: none; + padding:0 0 5px 0;; + margin:0; +} + +/* + Force border-box so that % widths fit the parent + container without overlap because of margin/padding. + + More Info : http://www.quirksmode.org/css/box.html +*/ +ul.sections > li > div { + -moz-box-sizing: border-box; /* firefox */ + -ms-box-sizing: border-box; /* ie */ + -webkit-box-sizing: border-box; /* webkit */ + -khtml-box-sizing: border-box; /* konqueror */ + box-sizing: border-box; /* css3 */ +} + + +/*---------------------- Jump Page -----------------------------*/ +#jump_to, #jump_page { + margin: 0; + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 16px Arial; + cursor: pointer; + text-align: right; + list-style: none; +} + +#jump_to a { + text-decoration: none; +} + +#jump_to a.large { + display: none; +} +#jump_to a.small { + font-size: 22px; + font-weight: bold; + color: #676767; +} + +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 10px 15px; + margin:0; +} + +#jump_wrapper { + display: none; + padding:0; +} + +#jump_to:hover #jump_wrapper { + display: block; +} + +#jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; +} + +#jump_page .source { + display: block; + padding: 15px; + text-decoration: none; + border-top: 1px solid #eee; +} + +#jump_page .source:hover { + background: #f5f5ff; +} + +#jump_page .source:first-child { +} + +/*---------------------- Low resolutions (> 320px) ---------------------*/ +@media only screen and (min-width: 320px) { + .pilwrap { display: none; } + + ul.sections > li > div { + display: block; + padding:5px 10px 0 10px; + } + + ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { + padding-left: 30px; + } + + ul.sections > li > div.content { + background: #f5f5ff; + overflow-x:auto; + -webkit-box-shadow: inset 0 0 5px #e5e5ee; + box-shadow: inset 0 0 5px #e5e5ee; + border: 1px solid #dedede; + margin:5px 10px 5px 10px; + padding-bottom: 5px; + } + + ul.sections > li > div.annotation pre { + margin: 7px 0 7px; + padding-left: 15px; + } + + ul.sections > li > div.annotation p tt, .annotation code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } +} + +/*---------------------- (> 481px) ---------------------*/ +@media only screen and (min-width: 481px) { + #container { + position: relative; + } + body { + background-color: #F5F5FF; + font-size: 15px; + line-height: 21px; + } + pre, tt, code { + line-height: 18px; + } + p, ul, ol { + margin: 0 0 15px; + } + + + #jump_to { + padding: 5px 10px; + } + #jump_wrapper { + padding: 0; + } + #jump_to, #jump_page { + font: 10px Arial; + text-transform: uppercase; + } + #jump_page .source { + padding: 5px 10px; + } + #jump_to a.large { + display: inline-block; + } + #jump_to a.small { + display: none; + } + + + + #background { + position: absolute; + top: 0; bottom: 0; + width: 350px; + background: #fff; + border-right: 1px solid #e5e5ee; + z-index: -1; + } + + ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { + padding-left: 40px; + } + + ul.sections > li { + white-space: nowrap; + } + + ul.sections > li > div { + display: inline-block; + } + + ul.sections > li > div.annotation { + max-width: 350px; + min-width: 350px; + min-height: 5px; + padding: 13px; + overflow-x: hidden; + white-space: normal; + vertical-align: top; + text-align: left; + } + ul.sections > li > div.annotation pre { + margin: 15px 0 15px; + padding-left: 15px; + } + + ul.sections > li > div.content { + padding: 13px; + vertical-align: top; + background: #f5f5ff; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + } + + .pilwrap { + position: relative; + display: inline; + } + + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + .for-h1 .pilcrow { + top: 47px; + } + .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow { + top: 35px; + } + + ul.sections > li > div.annotation:hover .pilcrow { + opacity: 1; + } +} + +/*---------------------- (> 1025px) ---------------------*/ +@media only screen and (min-width: 1025px) { + + body { + font-size: 16px; + line-height: 24px; + } + + #background { + width: 525px; + } + ul.sections > li > div.annotation { + max-width: 525px; + min-width: 525px; + padding: 10px 25px 1px 50px; + } + ul.sections > li > div.content { + padding: 9px 15px 16px 25px; + } +} + +/*---------------------- Syntax Highlighting -----------------------------*/ + +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +/* + +github.com style (c) Vasily Polovnyov + +*/ + +pre code { + display: block; padding: 0.5em; + color: #000; + background: #f8f8ff +} + +pre .comment, +pre .template_comment, +pre .diff .header, +pre .javadoc { + color: #408080; + font-style: italic +} + +pre .keyword, +pre .assignment, +pre .literal, +pre .css .rule .keyword, +pre .winutils, +pre .javascript .title, +pre .lisp .title, +pre .subst { + color: #954121; + /*font-weight: bold*/ +} + +pre .number, +pre .hexcolor { + color: #40a070 +} + +pre .string, +pre .tag .value, +pre .phpdoc, +pre .tex .formula { + color: #219161; +} + +pre .title, +pre .id { + color: #19469D; +} +pre .params { + color: #00F; +} + +pre .javascript .title, +pre .lisp .title, +pre .subst { + font-weight: normal +} + +pre .class .title, +pre .haskell .label, +pre .tex .command { + color: #458; + font-weight: bold +} + +pre .tag, +pre .tag .title, +pre .rules .property, +pre .django .tag .keyword { + color: #000080; + font-weight: normal +} + +pre .attribute, +pre .variable, +pre .instancevar, +pre .lisp .body { + color: #008080 +} + +pre .regexp { + color: #B68 +} + +pre .class { + color: #458; + font-weight: bold +} + +pre .symbol, +pre .ruby .symbol .string, +pre .ruby .symbol .keyword, +pre .ruby .symbol .keymethods, +pre .lisp .keyword, +pre .tex .special, +pre .input_number { + color: #990073 +} + +pre .builtin, +pre .constructor, +pre .built_in, +pre .lisp .title { + color: #0086b3 +} + +pre .preprocessor, +pre .pi, +pre .doctype, +pre .shebang, +pre .cdata { + color: #999; + font-weight: bold +} + +pre .deletion { + background: #fdd +} + +pre .addition { + background: #dfd +} + +pre .diff .change { + background: #0086b3 +} + +pre .chunk { + color: #aaa +} + +pre .tex .formula { + opacity: 0.5; +} diff --git a/docs/firechat.html b/docs/firechat.html new file mode 100644 index 0000000..12dbcb4 --- /dev/null +++ b/docs/firechat.html @@ -0,0 +1,1395 @@ + + + + + firechat.js + + + + + +
+
+ + +
+ + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..f7ac3d3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,271 @@ +--- +layout: docs +permalink: index.html +--- + + +### Overview + +Firechat is a simple, extensible chat widget powered by [Firebase](https://firebase.com/?utm_source=docs&utm_medium=site&utm_campaign=firechat). + +It is intended to serve as a concise, documented foundation for chat products built on Firebase. It works out of the box, and is easily extended. Fork the repo to start extending and customizing! + + + +### Getting Started + +Firechat works out of the box, provided that you include Firebase and Firechat in your application, and configure it to use your Firebase account. + +#### Prerequisites + +Before getting started, you'll need to: + +- [Download Firechat](https://github.com/firebase/firechat/archive/master.zip) +- Create a Firebase account (it's free) + +#### Adding Dependencies + +To get up and running using the default interface, include the following before the `` close tag on your page: + +{% highlight html %} + + + + + + +{% endhighlight %} + +#### Initializing Firechat + +To initialize Firechat, you must initialize Firebase, and then Firechat. Here is a typical setup: + +{% highlight html %} +
+ +{% endhighlight %} + +In the above example, `chatRef` should be an authenticated Firebase reference. More on this below. + + + +### Authentication + +Firechat uses Firebase [Authentication](https://firebase.com/docs/security/authentication.html?utm_source=docs&utm_medium=site&utm_campaign=firechat) and [Security Rules](https://firebase.com/docs/security/security-rules.html?utm_source=docs&utm_medium=site&utm_campaign=firechat), giving you the flexibility to authenticate with either your own custom authentication system or a number of built-in providers. + +#### Integrate Your Own Authentication + +If you already have authentication built into your application, you can integrate it with Firebase by generating your own JSON Web Tokens (JWT). + +Firebase provide helper libraries to generate these tokens for various languages, including: [.NET](https://github.com/firebase/firebase-token-generator-dotNet), [Java](https://github.com/firebase/firebase-token-generator-java), [Node.js](https://github.com/firebase/firebase-token-generator-node), [PHP](https://github.com/firebase/firebase-token-generator-php), [Python](https://github.com/firebase/firebase-token-generator-python), [Ruby](https://github.com/firebase/firebase-token-generator-ruby). + +Each token should be generated with an `id`: + + * The `id` is the unique identifier for the user (*string*). + +After generating the JWT, authenticate your Firebase reference: + +{% highlight javascript %} +var firechatRef = new Firebase('https://.firebaseio.com'); +firechatRef.auth(); +{% endhighlight %} + +For more information, check out the documentation for [Firebase Custom Login](https://www.firebase.com/docs/security/custom-login.html?utm_source=docs&utm_medium=site&utm_campaign=firechat). + +#### Delegate Authentication to Firebase Simple Login + +[Firebase Simple Login](https://www.firebase.com/docs/security/simple-login-overview.html?utm_source=docs&utm_medium=site&utm_campaign=firechat) is a built-in service that allows you to authenticate with [Facebook](https://www.firebase.com/docs/security/simple-login-facebook.html?utm_source=docs&utm_medium=site&utm_campaign=firechat), [Twitter](https://www.firebase.com/docs/security/simple-login-twitter.html?utm_source=docs&utm_medium=site&utm_campaign=firechat), [GitHub](https://www.firebase.com/docs/security/simple-login-github.html?utm_source=docs&utm_medium=site&utm_campaign=firechat), [Persona](https://www.firebase.com/docs/security/simple-login-persona.html?utm_source=docs&utm_medium=site&utm_campaign=firechat), or [email / password](https://www.firebase.com/docs/security/simple-login-email-password.html?utm_source=docs&utm_medium=site&utm_campaign=firechat) using only client-side code. + +* To begin, include the Firebase Simple Login script include in your page: + +{% highlight html %} + +{% endhighlight %} + +* Next, enable your provider of choice in your Firebase account, at `https://.firebaseio.com`. Social login services may require you to create and configure an application and an authorized origin for the request. + +* Lastly, authenticate the user on the client using your provider of choice: + +{% highlight javascript %} +var firechatRef = new Firebase('https://.firebaseio.com'); +var auth = new FirebaseSimpleLogin(firechatRef, function(error, user) { ... }); +... +auth.login('twitter'); // or 'facebook', 'github, 'persona', 'password' +{% endhighlight %} + +For more information, check out the documentation for [Firebase Simple Login](https://www.firebase.com/docs/security/simple-login-overview.html?utm_source=docs&utm_medium=site&utm_campaign=firechat). + + + +### Customizing Firechat + +Dive into the Firechat code to tweak the default interface or add a new one, change behavior or add new functionality. + +#### Code Structure + +* **`firechat.js`** is a conduit for data actions and bindings, allowing you to do things like enter or exit chat rooms, send and receive messages, create rooms and invite users to chat rooms, etc. Its sole dependency is Firebase. + +* **`firechat-ui.js`** is a full-fledged chat interface that demonstrates hooking into `firechat.js` and exposes a rich set of functionality to end users out-of-the-box. + +* **`rules.json`** defines a rule structure that maps to the data structure defined in `firechat.js`, defining both the data structure requirements and which users may read or write to which locations in Firebase. When uploaded to your Firebase, this configuration offers robust security to ensure that only properly authenticated users may chat, and neither user data nor private chat messages can be compromised. + +#### Modifying the Default UI + +The default Firechat UI is built using jQuery and Underscore.js, as well as Bootstrap for some styles and UI elements. To get started making changes, see `firechat-default.js` and `styles.less` to begin modifying the look and feel of the UI. + +When you're ready to build, simply execute `grunt` from the root directory of the project to compile your code into the combined output. + +#### Building a New UI + +To get started with a new UI layer, create a directory for your new interface under the `layouts` directory, using the name of your new interface. + +Next, create a primary JavaScript interface for your UI using the name `firechat-ui.js`, and add styles, layouts, and templates following the same convention as the default layout. + +Lastly, begin hooking into the Firechat API, detailed below, using the exposed methods and defined bindings. + +
Missing something? Send us a pull request and contribute to the repository!
+ + + +### Firechat API + +Firechat exposes a number of useful methods and bindings to initiate chat, enter and exit chat rooms, send invitations, create chat rooms, and send messages. + +#### Instantiating Firechat +{% highlight javascript %} +var chat = new Firechat(firebaseRef); +chat.setUser(userId, userName, function(user) { + chat.resumeSession(); +}); +{% endhighlight %} + + + +#### API - Public Methods + +`Firechat.setUser(userId, userName, onComplete)` + +> Initiates the authenticated connection to Firebase, loads any user metadata, +> and initializes Firebase listeners for chat events. + +`Firechat.resumeSession()` + +> Automatically re-enters any chat rooms that the user was previously in, if the +> user has history saved. + +`Firechat.on(eventType, callback)` + +> Sets up a binding for the specified event type (*string*), for which the +> callback will be invoked. See [API - Exposed Bindings](#api_bindings) +> for more information. + +`Firechat.createRoom(roomName, roomType, callback(roomId))` + +> Creates a new room with the given name (*string*) and type (*string* - `public` or `private`) and invokes the callback with the room ID on completion. + +`Firechat.enterRoom(roomId)` + +> Enters the chat room with the specified id. On success, all methods bound to the `room-enter` event will be invoked. + +`Firechat.leaveRoom(roomId)` + +> Leaves the chat room with the specified id. On success, all methods bound to the `room-exit` event will be invoked. + +`Firechat.sendMessage(roomId, messageContent, messageType='default', callback)` + +> Sends the message content to the room with the specified id and invokes the callback on completion. + +`Firechat.toggleUserMute(userId, callback)` + +> Mute or unmute a given user by id. + +`Firechat.inviteUser(userId, roomId)` + +> Invite a the specified user to the specific chat room. + +`Firechat.acceptInvite(inviteId, callback)` + +> Accept the specified invite, join the relevant chat room, and notify the user who sent it. + +`Firechat.declineInvite(inviteId, callback)` + +> Decline the specified invite and notify the user who sent it. + +`Firechat.getRoomList(callback)` + +> Fetch the list of all chat rooms. + +`Firechat.getUsersByRoom(roomId, [limit=100], callback)` + +> Fetch the list of users in the specified chat room, with an optional limit. + +`Firechat.getUsersByPrefix(prefix, startAt, endAt, limit, callback)` + +> Fetch the list of all active users, starting with the specified prefix, optionally between the specified startAt and endAt values, up to the optional, specified limit. + +`Firechat.getRoom(roomId, callback)` + +> Fetch the metadata for the specified chat room. + + + +#### API - Exposed Bindings + +To bind events to Firechat, invoke the public `on` method using an event ID and callback function. Public bindings are detailed below: + +> Supported event types include: + +> * `user-update` - Invoked when the user's metadata changes. +> * `room-enter` - Invoked when the user successfully enters a room. +> * `room-exit` - Invoked when the user exists a room. +> * `message-add` - Invoked when a new message is received. +> * `message-remove` - Invoked when a message is deleted. +> * `room-invite` - Invoked when a new room invite is received. +> * `room-invite-response` - Invoked when a response to a previous invite is received. + + + +### Data Structure + +Firechat uses [Firebase](https://www.firebase.com/?utm_source=docs&utm_medium=site&utm_campaign=firechat) for its data storage and synchronization. This means (a) you don't need to run any server code and (b) you get access to all the the Firebase features, including first-class data security, automatic scaling, and data portability. + +You own all of the data and can interact with it in a variety of ways. Firechat stores your data at the Firebase location you specify using the +following data structure: + +* `moderators/` + * `` - A list of user ids and their moderator status. + * `true|false` - A boolean value indicating the user's moderator status. +* `room-messages/` + * ` + * ` + * `userId` - The id of the user that sent the message. + * `name` - The name of the user that sent the message. + * `message` - The content of the message. + * `timestamp` - The time at which the message was sent. +* `room-metadata/` + * `` + * `createdAt` - The time at which the room was created. + * `createdByUserId`- The id of the user that created the room. + * `id` - The id of the room. + * `name` - The public display name of the room. + * `type` - The type of room, `public` or `private`. +* `room-users/` +* `user-names-online/` +* `users/` + * ` + * `id` - The id of the user. + * `name` - The display name of the user. + * `invites` - A list of invites the user has received. + * `muted` - A list of user ids currently muted by the user. + * `rooms` - A list of currently active rooms, used for sessioning. + +You may find it useful to interact directly with the Firebase data when building related features on your site. See the code or view the data in Forge (just enter your Firebase URL in a browser) for more details. + +### Security +To lock down your Firechat data, you can use Firebase's builtin +[Security features](https://www.firebase.com/docs/security-quickstart.html?utm_source=docs&utm_medium=site&utm_campaign=firechat). For some example +security rules for Firechat, see these [example rules on Github](https://github.com/firebase/firechat/tree/master/rules.json). diff --git a/docs/public/fonts/aller-bold.eot b/docs/public/fonts/aller-bold.eot new file mode 100755 index 0000000..1b32532 Binary files /dev/null and b/docs/public/fonts/aller-bold.eot differ diff --git a/docs/public/fonts/aller-bold.ttf b/docs/public/fonts/aller-bold.ttf new file mode 100755 index 0000000..dc4cc9c Binary files /dev/null and b/docs/public/fonts/aller-bold.ttf differ diff --git a/docs/public/fonts/aller-bold.woff b/docs/public/fonts/aller-bold.woff new file mode 100755 index 0000000..fa16fd0 Binary files /dev/null and b/docs/public/fonts/aller-bold.woff differ diff --git a/docs/public/fonts/aller-light.eot b/docs/public/fonts/aller-light.eot new file mode 100755 index 0000000..40bd654 Binary files /dev/null and b/docs/public/fonts/aller-light.eot differ diff --git a/docs/public/fonts/aller-light.ttf b/docs/public/fonts/aller-light.ttf new file mode 100755 index 0000000..c2c7290 Binary files /dev/null and b/docs/public/fonts/aller-light.ttf differ diff --git a/docs/public/fonts/aller-light.woff b/docs/public/fonts/aller-light.woff new file mode 100755 index 0000000..81a09d1 Binary files /dev/null and b/docs/public/fonts/aller-light.woff differ diff --git a/docs/public/fonts/novecento-bold.eot b/docs/public/fonts/novecento-bold.eot new file mode 100755 index 0000000..98a9a7f Binary files /dev/null and b/docs/public/fonts/novecento-bold.eot differ diff --git a/docs/public/fonts/novecento-bold.ttf b/docs/public/fonts/novecento-bold.ttf new file mode 100755 index 0000000..2af39b0 Binary files /dev/null and b/docs/public/fonts/novecento-bold.ttf differ diff --git a/docs/public/fonts/novecento-bold.woff b/docs/public/fonts/novecento-bold.woff new file mode 100755 index 0000000..de558b5 Binary files /dev/null and b/docs/public/fonts/novecento-bold.woff differ diff --git a/docs/public/stylesheets/normalize.css b/docs/public/stylesheets/normalize.css new file mode 100644 index 0000000..73abb76 --- /dev/null +++ b/docs/public/stylesheets/normalize.css @@ -0,0 +1,375 @@ +/*! normalize.css v2.0.1 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/* + * Corrects `block` display not defined in IE 8/9. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section, +summary { + display: block; +} + +/* + * Corrects `inline-block` display not defined in IE 8/9. + */ + +audio, +canvas, +video { + display: inline-block; +} + +/* + * Prevents modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/* + * Addresses styling for `hidden` attribute not present in IE 8/9. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/* + * 1. Sets default font family to sans-serif. + * 2. Prevents iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -ms-text-size-adjust: 100%; /* 2 */ +} + +/* + * Removes default margin. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/* + * Addresses `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/* + * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, + * Safari 5, and Chrome. + */ + +h1 { + font-size: 2em; +} + +/* + * Addresses styling not present in IE 8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/* + * Addresses styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + + +/* + * Corrects font family set oddly in Safari 5 and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +/* + * Improves readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* + * Sets consistent quote types. + */ + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +/* + * Addresses inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/* + * Prevents `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/* + * Removes border when inside `a` element in IE 8/9. + */ + +img { + border: 0; +} + +/* + * Corrects overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/* + * Addresses margin not present in IE 8/9 and Safari 5. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/* + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Corrects font family not being inherited in all browsers. + * 2. Corrects font size not being inherited in all browsers. + * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome + */ + +button, +input, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 2 */ + margin: 0; /* 3 */ +} + +/* + * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/* + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Corrects inability to style clickable `input` types in iOS. + * 3. Improves usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/* + * Re-set default cursor for disabled elements. + */ + +button[disabled], +input[disabled] { + cursor: default; +} + +/* + * 1. Addresses box sizing set to `content-box` in IE 8/9. + * 2. Removes excess padding in IE 8/9. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/* + * Removes inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* + * Removes inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE 8/9. + * 2. Improves readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/* + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/dist/0.1.0/firechat-default.css b/firechat/0.1.0/firechat-default.css similarity index 100% rename from dist/0.1.0/firechat-default.css rename to firechat/0.1.0/firechat-default.css diff --git a/dist/0.1.0/firechat-default.js b/firechat/0.1.0/firechat-default.js similarity index 100% rename from dist/0.1.0/firechat-default.js rename to firechat/0.1.0/firechat-default.js diff --git a/dist/0.1.0/firechat-default.min.css b/firechat/0.1.0/firechat-default.min.css similarity index 100% rename from dist/0.1.0/firechat-default.min.css rename to firechat/0.1.0/firechat-default.min.css diff --git a/dist/0.1.0/firechat-default.min.js b/firechat/0.1.0/firechat-default.min.js similarity index 100% rename from dist/0.1.0/firechat-default.min.js rename to firechat/0.1.0/firechat-default.min.js diff --git a/firechat/0.1.1/firechat-default.css b/firechat/0.1.1/firechat-default.css new file mode 100644 index 0000000..91a6c1a --- /dev/null +++ b/firechat/0.1.1/firechat-default.css @@ -0,0 +1,944 @@ +@charset "UTF-8"; +/* Boilerplate: Reset +============================================================ */ +#firechat div, +#firechat span, +#firechat applet, +#firechat object, +#firechat iframe, +#firechat h1, +#firechat h2, +#firechat h3, +#firechat h4, +#firechat h5, +#firechat h6, +#firechat p, +#firechat blockquote, +#firechat pre, +#firechat a, +#firechat abbr, +#firechat acronym, +#firechat address, +#firechat big, +#firechat cite, +#firechat code, +#firechat del, +#firechat dfn, +#firechat em, +#firechat img, +#firechat ins, +#firechat kbd, +#firechat q, +#firechat s, +#firechat samp, +#firechat small, +#firechat strike, +#firechat strong, +#firechat sub, +#firechat sup, +#firechat tt, +#firechat var, +#firechat b, +#firechat u, +#firechat i, +#firechat center, +#firechat dl, +#firechat dt, +#firechat dd, +#firechat ol, +#firechat ul, +#firechat li, +#firechat fieldset, +#firechat form, +#firechat label, +#firechat legend, +#firechat table, +#firechat caption, +#firechat tbody, +#firechat tfoot, +#firechat thead, +#firechat tr, +#firechat th, +#firechat td, +#firechat article, +#firechat aside, +#firechat canvas, +#firechat details, +#firechat embed, +#firechat figure, +#firechat figcaption, +#firechat footer, +#firechat header, +#firechat hgroup, +#firechat menu, +#firechat nav, +#firechat output, +#firechat ruby, +#firechat section, +#firechat summary, +#firechat time, +#firechat mark, +#firechat audio, +#firechat video { + border: 0; + font-size: 12px; + font-family: arial, helvetica, sans-serif; + vertical-align: baseline; + margin: 0; + padding: 0; +} +#firechat article, +#firechat aside, +#firechat details, +#firechat figcaption, +#firechat figure, +#firechat footer, +#firechat header, +#firechat hgroup, +#firechat menu, +#firechat nav, +#firechat section { + display: block; +} +#firechat body { + line-height: 1; +} +#firechat ol, +#firechat ul { + list-style: none; +} +#firechat blockquote, +#firechat q { + quotes: none; +} +#firechat blockquote:before, +#firechat blockquote:after, +#firechat q:before, +#firechat q:after { + content: none; +} +#firechat table { + border-collapse: collapse; + border-spacing: 0; +} +/* Boilerplate: Mixins +============================================================ */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +/* Boilerplate: Responsive Layout +============================================================ */ +#firechat { + color: #333; + text-align: left; +} +#firechat .center { + float: none !important; + margin-left: auto !important; + margin-right: auto !important; +} +#firechat .left { + float: left !important; +} +#firechat .right { + float: right !important; +} +#firechat .alignleft { + text-align: left !important; +} +#firechat .alignright { + text-align: right !important; +} +#firechat .aligncenter { + text-align: center !important; +} +#firechat .hidden { + display: none !important; +} +#firechat .row { + clear: both; +} +#firechat .fifth, +#firechat .fivesixth, +#firechat .fourfifth, +#firechat .half, +#firechat .ninetenth, +#firechat .quarter, +#firechat .sevententh, +#firechat .sixth, +#firechat .tenth, +#firechat .third, +#firechat .threefifth, +#firechat .threequarter, +#firechat .threetenth, +#firechat .twofifth, +#firechat .twothird, +#firechat .full { + margin-left: 2.127659574468085%; + float: left; + min-height: 1px; +} +#firechat .fifth:first-child, +#firechat .fivesixth:first-child, +#firechat .fourfifth:first-child, +#firechat .half:first-child, +#firechat .ninetenth:first-child, +#firechat .quarter:first-child, +#firechat .sevententh:first-child, +#firechat .sixth:first-child, +#firechat .tenth:first-child, +#firechat .third:first-child, +#firechat .threefifth:first-child, +#firechat .threequarter:first-child, +#firechat .threetenth:first-child, +#firechat .twofifth:first-child, +#firechat .twothird:first-child, +#firechat .full:first-child { + margin-left: 0; +} +#firechat .tenth { + width: 8.08510638297872%; +} +#firechat .sixth { + width: 14.893617021276595%; +} +#firechat .fifth { + width: 18.297872340425535%; +} +#firechat .quarter { + width: 23.404255319148938%; +} +#firechat .threetenth { + width: 26.3829787235%; +} +#firechat .third { + width: 31.914893617021278%; +} +#firechat .twofifth { + width: 38.72340425531915%; +} +#firechat .half { + width: 48.93617021276596%; +} +#firechat .sevententh { + width: 58.7234042555%; +} +#firechat .threefifth { + width: 59.14893617021278%; +} +#firechat .twothird { + width: 65.95744680851064%; +} +#firechat .threequarter { + width: 74.46808510638297%; +} +#firechat .ninetenth { + width: 74.8936170215%; +} +#firechat .fourfifth { + width: 79.57446808510639%; +} +#firechat .fivesixth { + width: 82.9787234042553%; +} +#firechat .full { + width: 100%; +} +#firechat .clipped { + overflow: hidden; +} +#firechat strong { + font-weight: bold; +} +#firechat em { + font-style: italic; +} +#firechat label { + display: block; +} +#firechat a { + color: #005580; +} +#firechat a:visited, +#firechat a:hover, +#firechat a:active { + color: #005580; +} +#firechat p { + margin: 10px 0; +} +#firechat h1, +#firechat h2, +#firechat h3, +#firechat h4, +#firechat h5, +#firechat h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; +} +#firechat h1, +#firechat h2, +#firechat h3 { + line-height: 40px; +} +#firechat h1 { + font-size: 38.5px; +} +#firechat h2 { + font-size: 31.5px; +} +#firechat h3 { + font-size: 24.5px; +} +#firechat h4 { + font-size: 17.5px; +} +#firechat h5 { + font-size: 14px; +} +#firechat h6 { + font-size: 11.9px; +} +#firechat small { + font-size: 90%; +} +/* Component: Tabs +============================================================ */ +#firechat .nav { + list-style: none; +} +#firechat .nav > li > a { + display: block; + background-color: #eeeeee; + text-decoration: none; + overflow: hidden; + white-space: nowrap; +} +#firechat .nav > li > a:hover, +#firechat .nav > li > a:focus { + background-color: #ffffff; +} +#firechat .nav-tabs { + border-bottom: 1px solid #ddd; + clear: both; +} +#firechat .nav-tabs > li { + float: left; + margin-bottom: -1px; + max-width: 45%; +} +#firechat .nav-tabs > li > a { + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topleft: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 4px; + padding: 4px 8px; + margin-right: 2px; + line-height: 20px; + border: 1px solid transparent; + border-color: #cccccc; +} +#firechat .nav-tabs > .active > a, +#firechat .nav-tabs > .active > a:hover, +#firechat .nav-tabs > .active > a:focus { + border-bottom-color: transparent; + background-color: #ffffff; + cursor: default; +} +#firechat .tab-content { + overflow: auto; +} +#firechat .tab-content > .tab-pane { + display: none; +} +#firechat .tab-content > .active { + display: block; + background-color: #ffffff; +} +/* Component: Dropdowns +============================================================ */ +#firechat .caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; + margin-top: 8px; + margin-left: 2px; +} +#firechat .dropdown { + position: relative; +} +#firechat .dropdown-toggle { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; +} +#firechat .dropdown-toggle:focus, +#firechat .dropdown-toggle:active { + outline: none; + text-decoration: none; +} +#firechat .dropdown-toggle.btn { + padding: 4px 0 0; + height: 22px; +} +#firechat .dropdown-menu { + *zoom: 1; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; + z-index: 1000; + display: none; + float: left; + position: absolute; + top: 100%; + left: 0; + width: 100%; + background-color: #ffffff; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + min-width: 98%; + padding: 0; + margin: -1px 0 0; +} +#firechat .dropdown-menu:before, +#firechat .dropdown-menu:after { + display: table; + content: ""; + line-height: 0; +} +#firechat .dropdown-menu:after { + clear: both; +} +#firechat .dropdown-menu ul { + background-color: #ffffff; + list-style: none; + overflow-y: scroll; + max-height: 300px; +} +#firechat .dropdown-menu ul > li > a { + display: block; + padding: 1px 1px 1px 3px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} +#firechat .dropdown-menu ul > li > a.highlight { + background-color: #d9edf7; +} +#firechat .dropdown-menu ul > li > a:hover, +#firechat .dropdown-menu ul > li > a:focus, +#firechat .dropdown-menu ul > .active > a, +#firechat .dropdown-menu ul > .active > a:hover, +#firechat .dropdown-menu ul > .active > a:focus { + text-decoration: none; + color: #000000; + background-color: #d9edf7; + outline: 0; +} +#firechat .dropdown-menu ul > .disabled > a, +#firechat .dropdown-menu ul > .disabled > a:hover, +#firechat .dropdown-menu ul > .disabled > a:focus { + color: #999999; + text-decoration: none; + background-color: transparent; + background-image: none; + cursor: default; +} +#firechat .dropdown-header { + position: relative; + width: 100%; + padding: 10px 0; + background-color: #eeeeee; + border-bottom: 1px solid #cccccc; +} +#firechat .dropdown-footer { + position: relative; + width: 100%; + padding: 10px 0px; + background-color: #eeeeee; + border-top: 1px solid #cccccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +#firechat .open { + *z-index: 1000; +} +#firechat .open > .dropdown-menu { + display: block; + border: 1px solid #cccccc; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; +} +#firechat .open > .dropdown-toggle { + outline: none; + text-decoration: none; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topleft: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 4px; +} +/* Component: Prompts +============================================================ */ +#firechat .prompt-wrapper { + position: absolute; + z-index: 1000; +} +#firechat .prompt { + position: absolute; + z-index: 1001; + background-color: #ffffff; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.45); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.45); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.45); +} +#firechat .prompt-header { + padding: 4px 8px; + font-weight: bold; + background-color: #eeeeee; + border: 1px solid #cccccc; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topleft: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 4px; +} +#firechat .prompt-header a.close { + opacity: 0.6; + font-size: 13px; + margin-top: 2px; +} +#firechat .prompt-header a.close:hover { + opacity: 0.9; +} +#firechat .prompt-body { + background-color: #ffffff; + padding: 4px 8px; + border-left: 1px solid #cccccc; + border-right: 1px solid #cccccc; +} +#firechat .prompt-footer { + padding: 4px 8px; + background-color: #eeeeee; + border: 1px solid #cccccc; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; +} +#firechat .prompt-background { + background-color: #333333; + border: 1px solid #333333; + opacity: 0.8; + z-index: 1000; + height: 100%; + width: 100%; +} +/* Component: Buttons +============================================================ */ +#firechat .btn { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + height: 24px; + display: inline-block; + *display: inline; + *zoom: 1; + padding: 2px 5px; + margin-bottom: 0; + text-align: center; + vertical-align: middle; + cursor: pointer; + color: #333333; + font-size: 12px; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e6e6e6; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + border: 1px solid #cccccc; + *border: 0; + border-bottom-color: #b3b3b3; + *margin-left: .3em; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} +#firechat .btn:hover, +#firechat .btn:focus, +#firechat .btn:active, +#firechat .btn.active, +#firechat .btn.disabled, +#firechat .btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; + outline: 0; +} +#firechat .btn:active, +#firechat .btn.active { + background-color: #cccccc; +} +#firechat .btn:first-child { + *margin-left: 0; +} +#firechat .btn:hover, +#firechat .btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +#firechat .btn.active, +#firechat .btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} +#firechat .btn.disabled, +#firechat .btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); +} +#firechat .btn.disabled:active, +#firechat .btn[disabled]:active { + -webkit-box-shadow: inherit; + -moz-box-shadow: inherit; + box-shadow: inherit; + background-color: #e6e6e6; +} +/* Component: Context Menu +============================================================ */ +#firechat .contextmenu { + position: fixed; + z-index: 1001; + min-width: 150px; + border: 1px solid #cccccc; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +#firechat .contextmenu ul { + background-color: #ffffff; + list-style: none; +} +#firechat .contextmenu ul > li > a { + display: block; + padding: 3px 10px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} +#firechat .contextmenu ul > li > a.highlight { + background-color: #d9edf7; +} +#firechat .contextmenu ul > li > a:hover, +#firechat .contextmenu ul > li > a:focus { + text-decoration: none; + color: #ffffff; + background-color: #0081c2; + outline: 0; +} +/* Custom Styles +============================================================ */ +#firechat { + padding: 0; + font-family: sans-serif; + font-size: 12px; + line-height: 18px; +} +#firechat input, +#firechat textarea { + width: 100%; + font-family: sans-serif; + font-size: 12px; + line-height: 18px; + padding: 2px 5px; + border: 1px solid #cccccc; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +#firechat input:-moz-placeholder, +#firechat textarea:-moz-placeholder { + color: #aaaaaa; +} +#firechat input:-ms-input-placeholder, +#firechat textarea:-ms-input-placeholder { + color: #aaaaaa; +} +#firechat input::-webkit-input-placeholder, +#firechat textarea::-webkit-input-placeholder { + color: #aaaaaa; +} +#firechat input[disabled], +#firechat textarea[disabled] { + background-color: #eeeeee; +} +#firechat input { + height: 24px; +} +#firechat textarea { + resize: none; + height: 40px; +} +#firechat .search-wrapper { + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; + border: 1px solid #cccccc; + margin: 0 5px; + padding: 2px 5px; + background: #ffffff; +} +#firechat .search-wrapper > input[type=text] { + padding-left: 0px; + border: none; +} +#firechat .search-wrapper > input[type=text]:focus, +#firechat .search-wrapper > input[type=text]:active { + outline: 0; +} +#firechat .chat { + overflow: auto; + -ms-overflow-x: hidden; + overflow-x: hidden; + height: 290px; + position: relative; + margin-bottom: 5px; + border: 1px solid #cccccc; + border-top: none; + overflow-y: scroll; +} +#firechat .chat textarea { + overflow: auto; + vertical-align: top; +} +#firechat .message { + color: #333; + padding: 3px 5px; + border-bottom: 1px solid #ccc; +} +#firechat .message.highlighted { + background-color: #d9edf7; +} +#firechat .message .name { + font-weight: bold; + overflow-x: hidden; +} +#firechat .message.message-self { + color: #2675ab; +} +#firechat .message:nth-child(odd) { + background-color: #f9f9f9; +} +#firechat .message:nth-child(odd).highlighted { + background-color: #d9edf7; +} +#firechat .message:nth-child(odd).message-local { + background-color: #effafc; +} +#firechat .message-content { + word-wrap: break-word; + padding-right: 45px; +} +#firechat .message-content.red { + color: red; +} +#firechat .message.message-notification .message-content { + font-style: italic; +} +#firechat ul::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; +} +#firechat ul::-webkit-scrollbar-thumb { + border-radius: 4px; + -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); +} +#firechat #firechat-header { + padding: 6px 0 0 0; + height: 40px; +} +#firechat #firechat-tabs { + height: 435px; +} +#firechat #firechat-tab-list { + background-color: #ffffff; +} +#firechat #firechat-tab-content { + width: 100%; + background-color: #ffffff; +} +#firechat .tab-pane-menu { + border: 1px solid #ccc; + border-top: none; + vertical-align: middle; + padding-bottom: 5px; +} +#firechat .tab-pane-menu .dropdown { + margin: 5px 0 0 5px; +} +#firechat .tab-pane-menu > .icon { + margin: 5px 2px 0; +} +#firechat .icon { + display: inline-block; + *margin-right: .3em; + line-height: 20px; + vertical-align: middle; + background-repeat: no-repeat; + padding: 0; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL8AAAANEAYAAACoeGM7AAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAM80lEQVR42t2cZ4xWRRfHZ3dhwbK7CJZYYiOxV+wlUQzCKmBBsWPvYjQiaiyxYUwULLErltgrNpRF4q5iV0BRLFE0diUi7LIq6Pqw74f//rzO4Zm9d+5z9c37ni//3OfemXtm5sypc58q55xzrrpauHSpcOONheedJ9x9d2Hv3sLvvxfefbfw5puFf/5Zvr+8VFUl7Ozs/rkNNxTef79w222F77wjPOoo4ccfF/O+UDvGzTVEf8wH1zU1wlJJePTRwv33Fx57rPDnn/1+wbzzG+IXvrhmPUPzY/kJEc/HzqslK1fLLy8cMUK4667CFVYQ/vqr8PXXhU884f8eL6eXXKJ2wqSd3tDY2KOHc84NGqSRnnCC7tbVCdvbhRMnasWnTdt5Z+eca2pK6797Yh6ef1641lrCPfYQfv21UNwl62qv115b2Nws/PZb4d57C3/7LftalaOQ/C63nPCII4Tsg6228scHffqp8LHHhPfe6/8eu49feEH44YfCq64S/v67cMGC7tuvu65w6FDhllsKkS/0zoQJaZzMmSO+JaEzZmgkY8b88Ydzzs2b19HhnHNffSWJ6tVLM1cq/fKLrjXiUaPUrn9/XZ9yip6rqVmyxDnnnnrKvPbAA4V6TTJxP/zgT4zdyC+9JOzXz++PgceSVSS1tUJtE+c22si/f+21Pj9Wwd54o//8ZpsJd9xR2KtX9+8vmkLzcsABPt9z5wr32af88xiOf4oaGoSHH17M+/LKg203aJDwiy+EVh5DyHzuuWcxfDn31lvOOffMM7Nn6z1SsZ2dn30m/OQT4eefC7/8UvjBB0LaVzav7D873vfeE6LQISvv3Of50P6OJetgWFp5ZeFzz5V/b1bEEd1333x80g+O4mWXCXEQMDB33CG8/XYhBhuDmMZnOn30kZ5/+23nnGtpefNN55wbP96Xl/nzhYsWCdvauEbeQP1Of6US/XRZfDz8Bx8U9uwpPPFE4eOPC/EMVl9dOG6ccORI4Q03CA87LHbA5RcCwgO47TbhokXCYcOERCKQ7GJiMLiPokeQ2QCjRwtvukkY6zHg0V10kXDAAH++EIz77hM++aRwxRWFq64q3H57v9/+/YVPPy2cOFF47rnCNE8kll/Gi4eIAWU9v/vOnz8MA4qjb18hG51+2JiLF+ebX/hA4U+b5t+/7jrhAw8IkQ88WvnPiZzi4Q0eXL6/dEJhawaGDydmk4uEQmhu1kj79tVImb8RI8TZwIG0p78ddnAubOhDhIcPIXd4nnh4OHYYzPXXF7K/ed5GBLb/NLKRFOtMfyCZAiIKns8aSVp9xP4i8nv//Ti+icjgl0iDeUsjpKDSyJYeqqs1A+3taDBpkn79Qi5YW9vf5wXq0YP+1L5U6loAUjpW4WPhQhN00EFC2SXnDj1UiAF44w2hTWnEEqkcCIWJYkShQNbDIIJZZx2h9Xhs/7EpFDwCNs6UKf584tGQsiFCGTJEuMEG/jxBzBe/H3+8cJddhBiAZ58thl/eM2uW8MgjhWyAyy8XojhRUGuuKayvF7Jhf/pJ+O67wltvFU6dGscv78fTgg45RPjII+XbYZA23bT8/Vtu8ecBDy9MpHS0EROFr1B76lQp7r32su20EwYMkGSOG4dq4I0a4fDh9G9TQN0TKR0iB8aD3G+9tRDHDoPPenKf53GYiABiPWm7f046SUgKDs8aQwxlVfiQTVXh0BChxir+kDol5WPv24im+Ahcb6iu9jVnR4euGX9oXsLj7GKYHP68eUI8ANsRA7WKE8sNkUopdvgJsW0QUBbc3oewl6GQs9LUDgr85JOF5OZHjRKyESZNEp55ppBIC7IbxgoSkQzt2OgXX1wMv9RCFBAuG4HgSREKU0vB42Jjo6gxtPvtJ8TzTM91+kQOf731hHj4KHybA0bRK8B1bpNNhAqkE8OH40CKLZ3I4a+0kq6l8JuapPAbGx991Lm/r5s8+aFDlWOdOVMuS0ODNm5bm1RWSwsSrJY2FZVGRFYYYgwt+wO5EpeJ4eaa+zxPexQ+/Wcl+iFCxeBvs43QRujW048lu09Y16KI/m3Ewu8h/VS55w/5b7AGMrbW5lyXImQh8EhtqGBDGOu525QDnm5RhOcBzZ8vfO01IYIaev/Mmf5zpALsfSiv5bYLfeqpQhSUrYHY92GYbD+shx0XKSOKlrFk1xHFTQRon7M1FNtepaOErwsvFBIqkzKKlQ8MDoTnCpFKwyCi2FZbTYhDgydO6gfaaaesnFC0xQcUUQwlBi6VpPCHDJGKmDyZGWMldY2nPmmSP5NEdlkJRYSCZr7FRbiIzTX3eZ72tiiclZjf444rzyd6hkid9+fNCNhxvfJKvn5C/YVy9nZVYw89ZCdfI6TVEuCLCI4Zhp+Ojq6FIAdLFR0PjpQOFpyFsQMidw5ZxRo7ATaEI6fMgFD8FJsxXCgee00qSiWPJNfKaYLp08u/P1YQ4ZfxWgWDAcOjZmMR8G+3nZBTPbwffhYuFJ5zjhCPKi9ZBUzkh8dpBTkk2KSKLr1UiAIJEYYhK2GQIOto4OGj8NdYQ4hck8tnvkmtQSGDHKK6On8nUNtIPHwUvp05SeSwYTI1s2YpJhk5kliO/uP4Qe6IxMnhk9JhXqyCtQ4Oz9Oe/nwzl05EtJbI5fMeTs8QscIP+zwtEuA+csz+fvjhOH5tf6SSoZCjEipeKw5MVj12PemZ9tXVvunt2dOuHE+K06oqvbG2lp2ikS1ZIknv3burO45lXn+9kKItOfzQwhO6jR0r5PiXPaUQmzNHkMllIyhMMBudmgKnjqxnwnSssooQhWpTLOR6UVwc/4ul1lYhx7rwRBF4PBEEwxLFavhkeTmmN2aMEAG3ghdrqHx149z555efx9DGY37vvFNoPcxQe1v0SyObe8cRIYf/4otCPHwUPsVge4yXWgTEumWl9nZtsPp6irbk8OVyTJ5sYyRJdGOjkitJjUP3FyzwNzIOV1ZivdinFG3J4dt5twqW9eJ5nUFKDIB1xNIIh8p6oijUa64RcnyT2SLSJHWXdtqKcdD/BRcIifBiCf44TIGe2WILYaiGhWHFoaFGxnxtvnlWDvwZY/yLF6toy+8LF2rGamrESXK8WRIwe7YcjKYmzdD06Xpi7lzFWqR4/1pYeywMT5ncL4rpiiuE9tgnIRyeozP9ZyVSBNZyskHxpFFUCGroOCf36Zf28MtzRCqxOXPaM24iJxsJQQgOCgyiqM4GJvduKZRbjOWXXH+fPkKKiUQmOnyYRBrwZeeXCIxxQ/mPSfpEkZn3KZO+7PHiH38U2mKurQHY478ooHSSaZswgWOZum5uFra2iqPOTs0g93EE4NzWAKZM4RioIoDx4/PNE4YQxWNTs6HUgL1Pe3sMNCuF9qOVG2penIPHYOHokcJ8+WVhS4sQR4NaJP3F7luI9tSALBFxovhBW6uonFSORm7mzMEAzJjhnHMNDdLI9fXgq68651xdnZLYffpY+cpIhLzkUNNySd98I7QKFPs0cGC+4dv3sCAYIE4bhXJboWvwrruExxwjJPWStyjD8/b0AyEiCt4Wh6xBpCi+225CG5kUdWogxC/HMyldshGJ7IhcWIfTTxciLxhWqKgcJxuPc/h2nTAAaYrq4IP9dni01jCEiVM3nIdWdaizUxuTDdvaSgQQ6kfPNTVhKPgOgP7j5id0Dh9DbR05Unlch54PfQeQRuyrUM7ZOoxkCq6+WkikhiOBIQ/JEx7sGWfE8QlZOYKyOi7sS+uQgdn3ra/4LT/ZSS1razEEfBgoOf0rdRUaIB4rXyCedpqQFAu5KzxdPyBJPPRlj7d1Tw89JLznHiGeb+hDFVIWIQPll9SS3ynKUUwj5RWbI6Q/inWVEgJsUxRFKdKi+T3rLKEtshf9IZytPdgIgNM75PA5dWQ9fBAFE0+cu+dDGQyAHwGMHi0P/sordT12LB4+Ct//0Cbvh1zWINr9gAePIYe4thGCbU//sYRHn/fDLBwyawhDOfe8ESbvCx3/zHuKJp5Cil8cVlVlxcjXhv5yICvh4aPwreDlJY5nUay1Cj2rINn2lD4qPX6KwcPzRQHhKeP5WeQ+ng2nSyhCcv4fKip1EuIXRQlfXIMUn7nPl9SkhKhFFM2v7QcDEIoAQoiHbxV+5V/uEgGQAuJLXRQ7X/KS0sHD///8cpcIl/P69MN3GDh2RBzUvvjLFVI2NuK1einty+CsZDMY/z0qyuOvkGwIY8+zMuE2ZYGHj8JHUWQlm/sm1ENRZ81dWrShLEVAy1/20F9EEZrTQnk9HJCNYL+YLMrD+F/jF7IbnBQQNQDOi/MFJ9fk8O26xisMQmb7OykaagD+p/RLl3JNDj+U0gn13z0xLhQ0BtEqfLtP7TXP057+YvcDCpvDInwvYInUcnIqqjLKK2/IydlnF8NHfvq3FP9/ANgTCJ9z9ZF7AAAAAElFTkSuQmCC) no-repeat top left; + opacity: 0.3; + font-size: 22px; + font-family: Arial; + font-weight: bold; + overflow: hidden; +} +#firechat .icon.plus { + margin-top: 0; + vertical-align: top; + background: transparent; +} +#firechat .icon.search { + background-position: 0 0; + width: 13px; + height: 13px; +} +#firechat .icon.close { + background-position: -120px 0; + width: 13px; + height: 13px; +} +#firechat .icon.user-chat { + background-position: -138px 0; + width: 17px; + height: 13px; +} +#firechat .icon.user-group { + background-position: -18px 0; + width: 17px; + height: 13px; +} +#firechat .icon.user-mute { + background-position: -84px 0; + width: 13px; + height: 13px; +} +#firechat .icon.user-mute.red { + background-position: -102px 0; + width: 13px; + height: 13px; +} +#firechat .icon:hover, +#firechat .btn:hover > .icon { + opacity: 0.6; +} +#firechat a > .icon { + margin: 3px 1px; +} diff --git a/firechat/0.1.1/firechat-default.js b/firechat/0.1.1/firechat-default.js new file mode 100644 index 0000000..4a372ff --- /dev/null +++ b/firechat/0.1.1/firechat-default.js @@ -0,0 +1,1786 @@ +(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); +this["FirechatDefaultTemplates"] = this["FirechatDefaultTemplates"] || {}; + +this["FirechatDefaultTemplates"]["templates/layout-full.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
\n
\n
\n\n\nChat Rooms\n\n\n
    \n
    \n\n\nVisitors\n\n\n
    \n
    \n
    \n\n\n
    \n
    \n
      \n
      \n
      \n
      \n
      \n
      \n
        \n
        \n
        \n
        ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/layout-popout.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
        \n
        \n
          \n
          \n
          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/message-context-menu.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
          \n\n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/message.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
          \n
          \n'; if (!isSelfMessage) { ;__p += '\n\n'; } ;__p += '
          \n
          \n' +((__t = ( message )) == null ? '' : __t) +'\n
          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-alert.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          ' +__e( message ) +'
          \n

          \n\n

          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-create-room.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          Give your chat room a name:
          \n\n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-invitation.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          ' +__e( fromUserName ) +'
          \n

          invited you to join

          \n
          ' +__e( toRoomName ) +'
          \n

          \n\n\n

          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-invite-private.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          Invite ' +__e( userName ) +' to ' +__e( roomName ) +'?
          \n

          \n\n\n

          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-invite-reply.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
          \n
          ' +__e( toUserName ) +'
          \n

          \n'; if (status === 'accepted') { ;__p += ' accepted your invite. '; } else { ;__p += ' declined your invite. '; } ;__p += '\n

          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-user-mute.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          ' +__e( userName ) +'
          \n

          \n\n\n

          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          \n' +__e( title ) +'\nX\n
          \n
          \n' +((__t = ( content )) == null ? '' : __t) +'\n
          \n
          \n
          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/room-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
        • \n\n' +__e( name ) +'\n\n
        • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/room-user-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
        • \n\n' +__e( name ) +''; if (!disableActions) { ;__p += '\n \n \n'; } ;__p += '\n\n
        • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/room-user-search-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
        • \n\n'; if (disableActions) { ;__p += '\n' +__e( name ) +'\n'; } else { ;__p += '\n' +__e( name ) +'\n+\n'; } ;__p += '\n\n
        • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/tab-content.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
          \n
          \n\n\nIn Room\n\n\n
          \n
            \n
            \n
            \n\n+\nInvite\n\n
            \n
            \n
            \n\n\n
            \n
            \n
              \n
              \n
              \n
              \n
              \n\n\n
              \n
              ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/tab-menu-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
            • \n' +__e( name ) +'\n
            • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/user-search-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
            • \n\n'; if (disableActions) { ;__p += '\n' +__e( name ) +'\n'; } else { ;__p += '\n' +__e( name ) +'\n \n'; } ;__p += '\n\n
            • ';}return __p}; +(function($) { + + // Shim for Function.bind(...) - (Required by IE < 9, FF < 4, SF < 6) + if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== "function") { + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + return fBound; + }; + } + + // Shim for Object.keys(...) - (Required by IE < 9, FF < 4) + Object.keys = Object.keys || function(oObj) { + var result = []; + for (var name in oObj) { + if (oObj.hasOwnProperty(name)) { + result.push(name); + } + } + return result; + }; + +})(); + +// Firechat is a simple, easily-extensible data layer for multi-user, +// multi-room chat, built entirely on [Firebase](https://firebase.com). +// +// The Firechat object is the primary conduit for all underlying data events. +// It exposes a number of methods for binding event listeners, creating, +// entering, or leaving chat rooms, initiating chats, sending messages, +// and moderator actions such as warning, kicking, or suspending users. +// +// Firechat.js 0.1.0 +// https://firebase.com +// (c) 2013 Firebase +// License: MIT + +// Setup +// -------------- +(function(Firebase) { + + // Establish a reference to the `window` object, and save the previous value + // of the `Firechat` variable. + var root = this, + previousFirechat = root.Firechat; + + function Firechat(firebaseRef, options) { + + // Instantiate a new connection to Firebase. + this._firebase = firebaseRef; + + // User-specific instance variables. + this._user = null; + this._userId = null; + this._userName = null; + this._isModerator = false; + + // A unique id generated for each session. + this._sessionId = null; + + // A mapping of event IDs to an array of callbacks. + this._events = {}; + + // A mapping of room IDs to a boolean indicating presence. + this._rooms = {}; + + // A mapping of operations to re-queue on disconnect. + this._presenceBits = {}; + + // Commonly-used Firebase references. + this._userRef = null; + this._messageRef = this._firebase.child('room-messages'); + this._roomRef = this._firebase.child('room-metadata'); + this._privateRoomRef = this._firebase.child('room-private-metadata'); + this._moderatorsRef = this._firebase.child('moderators'); + this._suspensionsRef = this._firebase.child('suspensions'); + this._usersOnlineRef = this._firebase.child('user-names-online'); + + // Setup and establish default options. + this._options = options || {}; + + // The number of historical messages to load per room. + this._options.numMaxMessages = this._options.numMaxMessages || 50; + } + + // Run Firechat in *noConflict* mode, returning the `Firechat` variable to + // its previous owner, and returning a reference to the Firechat object. + Firechat.noConflict = function noConflict() { + root.Firechat = previousFirechat; + return Firechat; + }; + + // Export the Firechat object as a global. + root.Firechat = Firechat; + + // Firechat Internal Methods + // -------------- + Firechat.prototype = { + + // Load the initial metadata for the user's account and set initial state. + _loadUserMetadata: function(onComplete) { + var self = this; + + // Update the user record with a default name on user's first visit. + this._userRef.transaction(function(current) { + if (!current || !current.id || !current.name) { + return { + id: self._userId, + name: self._userName + }; + } + }, function(error, committed, snapshot) { + self._user = snapshot.val(); + self._moderatorsRef.child(self._userId).once('value', function(snapshot) { + self._isModerator = !!snapshot.val(); + root.setTimeout(onComplete, 0); + }); + }); + }, + + // Initialize Firebase listeners and callbacks for the supported bindings. + _setupDataEvents: function() { + // Monitor connection state so we can requeue disconnect operations if need be. + this._firebase.root().child('.info/connected').on('value', function(snapshot) { + if (snapshot.val() === true) { + // We're connected (or reconnected)! Set up our presence state. + for (var i = 0; i < this._presenceBits; i++) { + var op = this._presenceBits[i], + ref = this._firebase.root().child(op.ref); + + ref.onDisconnect().set(op.offlineValue); + ref.set(op.onlineValue); + } + } + }, this); + + // Generate a unique session id for the visit. + var sessionRef = this._userRef.child('sessions').push(); + this._sessionId = sessionRef.name(); + this._queuePresenceOperation(sessionRef, true, null); + + // Register our username in the public user listing. + var usernameRef = this._usersOnlineRef.child(this._userName.toLowerCase()); + var usernameSessionRef = usernameRef.child(this._sessionId); + this._queuePresenceOperation(usernameSessionRef, { + id: this._userId, + name: this._userName + }, null); + + // Listen for state changes for the given user. + this._userRef.on('value', this._onUpdateUser, this); + + // Listen for chat invitations from other users. + this._userRef.child('invites').on('child_added', this._onFirechatInvite, this); + + // Listen for messages from moderators and adminstrators. + this._userRef.child('notifications').on('child_added', this._onNotification, this); + }, + + // Append the new callback to our list of event handlers. + _addEventCallback: function(eventId, callback) { + this._events[eventId] = this._events[eventId] || []; + this._events[eventId].push(callback); + }, + + // Retrieve the list of event handlers for a given event id. + _getEventCallbacks: function(eventId) { + if (this._events.hasOwnProperty(eventId)) { + return this._events[eventId]; + } + return []; + }, + + // Invoke each of the event handlers for a given event id with specified data. + _invokeEventCallbacks: function(eventId) { + var args = [], + callbacks = this._getEventCallbacks(eventId); + + Array.prototype.push.apply(args, arguments); + args = args.slice(1); + + for (var i = 0; i < callbacks.length; i += 1) { + callbacks[i].apply(null, args); + } + }, + + // Keep track of on-disconnect events so they can be requeued if we disconnect the reconnect. + _queuePresenceOperation: function(ref, onlineValue, offlineValue) { + ref.onDisconnect().set(offlineValue); + ref.set(onlineValue); + this._presenceBits[ref.toString()] = { + ref: ref, + onlineValue: onlineValue, + offlineValue: offlineValue + }; + }, + + // Remove an on-disconnect event from firing upon future disconnect and reconnect. + _removePresenceOperation: function(path, value) { + var ref = new Firebase(path); + ref.onDisconnect().cancel(); + ref.set(value); + delete this._presenceBits[path]; + }, + + // Event to monitor user current user state. + _onUpdateUser: function(snapshot) { + this._user = snapshot.val(); + this._invokeEventCallbacks('user-update', this._user); + }, + + // Events to monitor room entry / exit and messages additional / removal. + _onEnterRoom: function(room) { + this._invokeEventCallbacks('room-enter', room); + }, + _onNewMessage: function(roomId, snapshot) { + var message = snapshot.val(); + message.id = snapshot.name(); + this._invokeEventCallbacks('message-add', roomId, message); + }, + _onRemoveMessage: function(roomId, snapshot) { + var messageId = snapshot.name(); + this._invokeEventCallbacks('message-remove', roomId, messageId); + }, + _onLeaveRoom: function(roomId) { + this._invokeEventCallbacks('room-exit', roomId); + }, + + // Event to listen for notifications from administrators and moderators. + _onNotification: function(snapshot) { + var notification = snapshot.val(); + if (!notification.read) { + if (notification.notificationType !== 'suspension' || notification.data.suspendedUntil < Firebase.ServerValue.TIMESTAMP) { + snapshot.ref().child('read').set(true); + } + this._invokeEventCallbacks('notification', notification); + } + }, + + // Events to monitor chat invitations and invitation replies. + _onFirechatInvite: function(snapshot) { + var self = this, + invite = snapshot.val(); + + // Skip invites we've already responded to. + if (invite.status) { + return; + } + + invite.id = invite.id || snapshot.name(); + self.getRoom(invite.roomId, function(room) { + invite.toRoomName = room.name; + self._invokeEventCallbacks('room-invite', invite); + }); + }, + _onFirechatInviteResponse: function(snapshot) { + var self = this, + invite = snapshot.val(); + + invite.id = invite.id || snapshot.name(); + this._invokeEventCallbacks('room-invite-response', invite); + } + }; + + // Firechat External Methods + // -------------- + + // Initialize the library and setup data listeners. + Firechat.prototype.initWithUser = function(userId, userName, callback) { + var self = this; + + self._firebase.root().child('.info/authenticated').on('value', function(snapshot) { + if (snapshot.val() === true) { + self._firebase.root().child('.info/authenticated').off(); + + self._userId = userId.toString(); + self._userName = userName.toString(); + self._userRef = self._firebase.child('users').child(self._userId); + self._loadUserMetadata(function() { + root.setTimeout(function() { + callback(self._user); + self._setupDataEvents(); + }, 0); + }); + } + }); + }; + + // Resumes the previous session by automatically entering rooms. + Firechat.prototype.resumeSession = function() { + this._userRef.child('rooms').once('value', function(snapshot) { + var rooms = snapshot.val(); + for (var roomId in rooms) { + this.enterRoom(rooms[roomId].id); + } + }, /* onError */ function(){}, /* context */ this); + }; + + // Callback registration. Supports each of the following events: + Firechat.prototype.on = function(eventType, cb) { + this._addEventCallback(eventType, cb); + }; + + // Create and automatically enter a new chat room. + Firechat.prototype.createRoom = function(roomName, roomType, callback) { + var self = this, + newRoomRef = this._roomRef.push(); + + var newRoom = { + id: newRoomRef.name(), + name: roomName, + type: roomType || 'public', + createdByUserId: this._userId, + createdAt: Firebase.ServerValue.TIMESTAMP + }; + + if (roomType === 'private') { + newRoom.authorizedUsers = {}; + newRoom.authorizedUsers[this._userId] = true; + } + + newRoomRef.set(newRoom, function(error) { + if (!error) { + self.enterRoom(newRoomRef.name()); + } + if (callback) { + callback(newRoomRef.name()); + } + }); + }; + + // Enter a chat room. + Firechat.prototype.enterRoom = function(roomId) { + var self = this; + self.getRoom(roomId, function(room) { + var roomName = room.name; + + if (!roomId || !roomName) return; + + // Skip if we're already in this room. + if (self._rooms[roomId]) { + return; + } + + self._rooms[roomId] = true; + + // Save entering this room to resume the session again later. + self._userRef.child('rooms').child(roomId).set({ + id: roomId, + name: roomName, + active: true + }); + + // Set presence bit for the room and queue it for removal on disconnect. + var presenceRef = self._firebase.child('room-users').child(roomId).child(self._userId).child(self._sessionId); + self._queuePresenceOperation(presenceRef, { + id: self._userId, + name: self._userName + }, null); + + // Invoke our callbacks before we start listening for new messages. + self._onEnterRoom({ id: roomId, name: roomName }); + + // Setup message listeners + self._roomRef.child(roomId).once('value', function(snapshot) { + self._messageRef.child(roomId).limit(self._options.numMaxMessages).on('child_added', function(snapshot) { + self._onNewMessage(roomId, snapshot); + }, /* onCancel */ function() { + // Turns out we don't have permission to access these messages. + self.leaveRoom(roomId); + }, /* context */ self); + + self._messageRef.child(roomId).limit(self._options.numMaxMessages).on('child_removed', function(snapshot) { + self._onRemoveMessage(roomId, snapshot); + }, /* onCancel */ function(){}, /* context */ self); + }, /* onFailure */ function(){}, self); + }); + }; + + // Leave a chat room. + Firechat.prototype.leaveRoom = function(roomId) { + var self = this, + userRoomRef = self._firebase.child('room-users').child(roomId), + presenceRef = userRoomRef.child(self._userId).child(self._sessionId); + + // Remove listener for new messages to this room. + self._messageRef.child(roomId).off(); + + // Remove presence bit for the room and cancel on-disconnect removal. + self._removePresenceOperation(presenceRef.toString(), null); + + // Remove session bit for the room. + self._userRef.child('rooms').child(roomId).remove(); + + delete self._rooms[roomId]; + + // Invoke event callbacks for the room-exit event. + self._onLeaveRoom(roomId); + }; + + Firechat.prototype.sendMessage = function(roomId, messageContent, messageType, cb) { + var self = this, + message = { + userId: self._userId, + name: self._userName, + timestamp: Firebase.ServerValue.TIMESTAMP, + message: messageContent, + type: messageType || 'default' + }, + newMessageRef = self._messageRef.child(roomId).push(); + + newMessageRef.setWithPriority(message, Firebase.ServerValue.TIMESTAMP, cb); + }; + + Firechat.prototype.deleteMessage = function(roomId, messageId, cb) { + var self = this; + + self._messageRef.child(roomId).child(messageId).remove(cb); + }; + + // Mute or unmute a given user by id. This list will be stored internally and + // all messages from the muted clients will be filtered client-side after + // receipt of each new message. + Firechat.prototype.toggleUserMute = function(userId, cb) { + var self = this; + + self._userRef.child('muted').child(userId).transaction(function(isMuted) { + return (isMuted) ? null : true; + }, cb); + }; + + // Send a moderator notification to a specific user. + Firechat.prototype.sendSuperuserNotification = function(userId, notificationType, data, cb) { + var self = this, + userNotificationsRef = self._firebase.child('users').child(userId).child('notifications'); + + userNotificationsRef.push({ + fromUserId: self._userId, + timestamp: Firebase.ServerValue.TIMESTAMP, + notificationType: notificationType, + data: data || {} + }, cb); + }; + + // Warn a user for violating the terms of service or being abusive. + Firechat.prototype.warnUser = function(userId) { + var self = this; + + self.sendSuperuserNotification(userId, 'warning'); + }; + + // Suspend a user by putting the user into read-only mode for a period. + Firechat.prototype.suspendUser = function(userId, timeLengthSeconds, cb) { + var self = this, + suspendedUntil = Firebase.ServerValue.TIMESTAMP + 1000*timeLengthSeconds; + + self._suspensionsRef.child(userId).set(suspendedUntil, function(error) { + if (error && cb) { + return cb(error); + } else { + self.sendSuperuserNotification(userId, 'suspension', { + suspendedUntil: suspendedUntil + }); + return cb(null); + } + }); + }; + + // Invite a user to a specific chat room. + Firechat.prototype.inviteUser = function(userId, roomId) { + var self = this, + sendInvite = function() { + var inviteRef = self._firebase.child('users').child(userId).child('invites').push(); + inviteRef.set({ + id: inviteRef.name(), + fromUserId: self._userId, + fromUserName: self._userName, + toRoomId: roomId + }); + + // Handle listen unauth / failure in case we're kicked. + inviteRef.on('value', self._onFirechatInviteResponse, function(){}, self); + }; + + self.getRoom(roomId, function(room) { + if (room.type === 'private') { + var authorizedUserRef = self._roomRef.child(roomId).child('authorizedUsers'); + authorizedUserRef.child(userId).set(true, function(error) { + if (!error) { + sendInvite(); + } + }); + } else { + sendInvite(); + } + }); + }; + + Firechat.prototype.acceptInvite = function(inviteId, cb) { + var self = this; + + self._userRef.child('invites').child(inviteId).once('value', function(snapshot) { + var invite = snapshot.val(); + if (invite === null && cb) { + return cb(new Error('acceptInvite(' + inviteId + '): invalid invite id')); + } else { + self.enterRoom(invite.toRoomId); + self._userRef.child('invites').child(inviteId).update({ + 'status': 'accepted', + 'toUserName': self._userName + }, cb); + } + }, self); + }; + + Firechat.prototype.declineInvite = function(inviteId, cb) { + var self = this, + updates = { + 'status': 'declined', + 'toUserName': self._userName + }; + + self._userRef.child('invites').child(inviteId).update(updates, cb); + }; + + Firechat.prototype.getRoomList = function(cb) { + var self = this; + + self._roomRef.once('value', function(snapshot) { + cb(snapshot.val()); + }); + }; + + Firechat.prototype.getUsersByRoom = function() { + var self = this, + roomId = arguments[0], + query = self._firebase.child('room-users').child(roomId), + cb = arguments[arguments.length - 1], + limit = null; + + if (arguments.length > 2) { + limit = arguments[1]; + } + + query = (limit) ? query.limit(limit) : query; + + query.once('value', function(snapshot) { + var usernames = snapshot.val() || {}, + usernamesUnique = {}; + + for (var username in usernames) { + for (var session in usernames[username]) { + // Skip all other sessions for this user as we only need one. + usernamesUnique[username] = usernames[username][session]; + break; + } + } + + root.setTimeout(function() { + cb(usernamesUnique); + }, 0); + }); + }; + + Firechat.prototype.getUsersByPrefix = function(prefix, startAt, endAt, limit, cb) { + var self = this, + query = this._usersOnlineRef, + prefixLower = prefix.toLowerCase(); + + if (startAt) { + query = query.startAt(null, startAt); + } else if (endAt) { + query = query.endAt(null, endAt); + } else { + query = (prefixLower) ? query.startAt(null, prefixLower) : query.startAt(); + } + + query = (limit) ? query.limit(limit) : query; + + query.once('value', function(snapshot) { + var usernames = snapshot.val() || {}, + usernamesFiltered = {}; + + for (var userNameKey in usernames) { + var sessions = usernames[userNameKey], + userName, userId, usernameClean; + + // Grab the user data from the first registered active session. + for (var sessionId in sessions) { + userName = sessions[sessionId].name; + userId = sessions[sessionId].id; + + // Skip all other sessions for this user as we only need one. + break; + } + + // Filter out any usernames that don't match our prefix and break. + if ((prefix.length > 0) && (userName.toLowerCase().indexOf(prefixLower) !== 0)) + continue; + + usernamesFiltered[userName] = { + name: userName, + id: userId + }; + } + + root.setTimeout(function() { + cb(usernamesFiltered); + }, 0); + }); + }; + + // Miscellaneous helper methods. + Firechat.prototype.getRoom = function(roomId, callback) { + this._roomRef.child(roomId).once('value', function(snapshot) { + callback(snapshot.val()); + }); + }; + + Firechat.prototype.userIsModerator = function() { + return this._isModerator; + }; + + Firechat.prototype.sessionIdGet = function() { + return this._sessionId; + }; +})(Firebase); +(function($) { + + + if (!$ || (parseInt($().jquery.replace(/\./g, ""), 10) < 170)) { + throw new Error("jQuery 1.7 or later required!"); + } + + var root = this, + previousFirechatUI = root.FirechatUI; + + root.FirechatUI = FirechatUI; + + if (!self.FirechatDefaultTemplates) { + throw new Error("Unable to find chat templates!"); + } + + function FirechatUI(firebaseRef, el, options) { + var self = this; + + if (!firebaseRef) { + throw new Error('FirechatUI: Missing required argument `firebaseRef`'); + } + + if (!el) { + throw new Error('FirechatUI: Missing required argument `el`'); + } + + options = options || {}; + this._options = options; + + this._el = el; + this._user = null; + this._chat = new Firechat(firebaseRef, options); + + // A list of rooms to enter once we've made room for them (once we've hit the max room limit). + this._roomQueue = []; + + // Define some constants regarding maximum lengths, client-enforced. + this.maxLengthUsername = 15; + this.maxLengthUsernameDisplay = 15; + this.maxLengthRoomName = 24; + this.maxLengthMessage = 120; + this.maxUserSearchResults = 100; + + // Define some useful regexes. + this.urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim; + this.pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim; + + this._renderLayout(); + + // Grab shortcuts to commonly used jQuery elements. + this.$wrapper = $('#firechat'); + this.$roomList = $('#firechat-room-list'); + this.$tabList = $('#firechat-tab-list'); + this.$tabContent = $('#firechat-tab-content'); + this.$messages = {}; + + // Rate limit messages from a given user with some defaults. + this.$rateLimit = { + limitCount: 10, // max number of events + limitInterval: 10000, // max interval for above count in milliseconds + limitWaitTime: 30000, // wait time if a user hits the wait limit + history: {} + }; + + // Setup UI bindings for chat controls. + this._bindUIEvents(); + + // Setup bindings to internal methods + this._bindDataEvents(); + } + + // Run FirechatUI in *noConflict* mode, returning the `FirechatUI` variable to + // its previous owner, and returning a reference to the FirechatUI object. + FirechatUI.noConflict = function noConflict() { + root.FirechatUI = previousFirechatUI; + return FirechatUI; + }; + + FirechatUI.prototype = { + + _bindUIEvents: function() { + // Chat-specific custom interactions and functionality. + this._bindForHeightChange(); + this._bindForTabControls(); + this._bindForRoomList(); + this._bindForUserRoomList(); + this._bindForUserSearch(); + this._bindForUserMuting(); + this._bindForChatInvites(); + this._bindForRoomListing(); + + // Generic, non-chat-specific interactive elements. + this._setupTabs(); + this._setupDropdowns(); + this._bindTextInputFieldLimits(); + }, + + _bindDataEvents: function() { + this._chat.on('user-update', this._onUpdateUser.bind(this)); + + // Bind events for new messages, enter / leaving rooms, and user metadata. + this._chat.on('room-enter', this._onEnterRoom.bind(this)); + this._chat.on('room-exit', this._onLeaveRoom.bind(this)); + this._chat.on('message-add', this._onNewMessage.bind(this)); + this._chat.on('message-remove', this._onRemoveMessage.bind(this)); + + // Bind events related to chat invitations. + this._chat.on('room-invite', this._onChatInvite.bind(this)); + this._chat.on('room-invite-response', this._onChatInviteResponse.bind(this)); + + // Binds events related to admin or moderator notifications. + this._chat.on('notification', this._onNotification.bind(this)); + }, + + _renderLayout: function() { + var template = FirechatDefaultTemplates["templates/layout-full.html"]; + $(this._el).html(template({ + maxLengthUsername: this.maxLengthUsername + })); + }, + + _onUpdateUser: function(user) { + // Update our current user state and render latest user name. + this._user = user; + + // Update our interface to reflect which users are muted or not. + var mutedUsers = this._user.muted || {}; + $('[data-event="firechat-user-mute-toggle"]').each(function(i, el) { + var userId = $(this).closest('[data-user-id]').data('user-id'); + $(this).toggleClass('red', !!mutedUsers[userId]); + }); + + // Ensure that all messages from muted users are removed. + for (var userId in mutedUsers) { + $('.message[data-user-id="' + userId + '"]').fadeOut(); + } + }, + _onEnterRoom: function(room) { + this.attachTab(room.id, room.name); + }, + _onLeaveRoom: function(roomId) { + this.removeTab(roomId); + + // Auto-enter rooms in the queue + if ((this._roomQueue.length > 0)) { + this._chat.enterRoom(this._roomQueue.shift(roomId)); + } + }, + _onNewMessage: function(roomId, message) { + var userId = message.userId; + if (!this._user.muted || !this._user.muted[userId]) { + this.showMessage(roomId, message); + } + }, + _onRemoveMessage: function(roomId, messageId) { + this.removeMessage(roomId, messageId); + }, + + // Events related to chat invitations. + _onChatInvite: function(invitation) { + var self = this; + var template = FirechatDefaultTemplates["templates/prompt-invitation.html"]; + var $prompt = this.prompt('Invite', template(invitation)); + $prompt.find('a.close').click(function() { + $prompt.remove(); + self._chat.declineInvite(invitation.id); + }); + + $prompt.find('[data-toggle=accept]').click(function() { + $prompt.remove(); + self._chat.acceptInvite(invitation.id); + }); + + $prompt.find('[data-toggle=decline]').click(function() { + $prompt.remove(); + self._chat.declineInvite(invitation.id); + }); + }, + _onChatInviteResponse: function(invitation) { + if (!invitation.status) return; + + var self = this, + template = FirechatDefaultTemplates["templates/prompt-invite-reply.html"], + $prompt; + + if (invitation.status && invitation.status === 'accepted') { + $prompt = this.prompt('Accepted', template(invitation)); + this._chat.getRoom(invitation.toRoomId, function(room) { + self.attachTab(invitation.toRoomId, room.name); + }); + } else { + $prompt = this.prompt('Declined', template(invitation)); + } + + $prompt.find('a.close').click(function() { + $prompt.remove(); + }); + }, + + // Events related to admin or moderator notifications. + _onNotification: function(notification) { + if (notification.notificationType === 'warning') { + this.renderAlertPrompt('Warning', 'You are being warned for inappropriate messaging. Further violation may result in temporary or permanent ban of service.'); + } else if (notification.notificationType === 'suspension') { + var suspendedUntil = notification.data.suspendedUntil, + secondsLeft = Math.round((suspendedUntil - new Date().getTime()) / 1000), + timeLeft = ''; + + if (secondsLeft > 0) { + if (secondsLeft > 2*3600) { + var hours = Math.floor(secondsLeft / 3600); + timeLeft = hours + ' hours, '; + secondsLeft -= 3600*hours; + } + timeLeft += Math.floor(secondsLeft / 60) + ' minutes'; + this.renderAlertPrompt('Suspended', 'A moderator has suspended you for violating site rules. You cannot send messages for another ' + timeLeft + '.'); + } + } + } + }; + + FirechatUI.prototype.initWithUser = function(userId, userName) { + var self = this; + + // Initialize data events + self._chat.initWithUser(userId, userName, function(user) { + self._user = user; + + if (self._chat.userIsModerator()) { + self._bindSuperuserUIEvents(); + } + + self._chat.resumeSession(); + }); + }; + + /** + * Binds a custom context menu to messages for superusers to warn or ban + * users for violating terms of service. + */ + FirechatUI.prototype._bindSuperuserUIEvents = function() { + var self = this, + parseMessageVars = function(event) { + var $this = $(this), + messageId = $this.closest('[data-message-id]').data('message-id'), + userId = $('[data-message-id="' + messageId + '"]').closest('[data-user-id]').data('user-id'), + roomId = $('[data-message-id="' + messageId + '"]').closest('[data-room-id]').data('room-id'); + + return { messageId: messageId, userId: userId, roomId: roomId }; + }, + clearMessageContextMenus = function() { + // Remove any context menus currently showing. + $('[data-toggle="firechat-contextmenu"]').each(function() { + $(this).remove(); + }); + + // Remove any messages currently highlighted. + $('#firechat .message.highlighted').each(function() { + $(this).removeClass('highlighted'); + }); + }, + showMessageContextMenu = function(event) { + var $this = $(this), + $message = $this.closest('[data-message-id]'), + template = FirechatDefaultTemplates["templates/message-context-menu.html"], + messageVars = parseMessageVars.call(this, event), + $template; + + event.preventDefault(); + + // Clear existing menus. + clearMessageContextMenus(); + + // Highlight the relevant message. + $this.addClass('highlighted'); + + self._chat.getRoom(messageVars.roomId, function(room) { + // Show the context menu. + $template = $(template({ + id: $message.data('message-id') + })); + $template.css({ + left: event.clientX, + top: event.clientY + }).appendTo(self.$wrapper); + }); + }; + + // Handle dismissal of message context menus (any non-right-click click event). + $(document).bind('click', { self: this }, function(event) { + if (!event.button || event.button != 2) { + clearMessageContextMenus(); + } + }); + + // Handle display of message context menus (via right-click on a message). + $(document).delegate('[data-class="firechat-message"]', 'contextmenu', showMessageContextMenu); + + // Handle click of the 'Warn User' contextmenu item. + $(document).delegate('[data-event="firechat-user-warn"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.warnUser(messageVars.userId); + }); + + // Handle click of the 'Suspend User (1 Hour)' contextmenu item. + $(document).delegate('[data-event="firechat-user-suspend-hour"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.suspendUser(messageVars.userId, /* 1 Hour = 3600s */ 60*60); + }); + + // Handle click of the 'Suspend User (1 Day)' contextmenu item. + $(document).delegate('[data-event="firechat-user-suspend-day"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.suspendUser(messageVars.userId, /* 1 Day = 86400s */ 24*60*60); + }); + + // Handle click of the 'Delete Message' contextmenu item. + $(document).delegate('[data-event="firechat-message-delete"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.deleteMessage(messageVars.roomId, messageVars.messageId); + }); + }; + + /** + * Binds to height changes in the surrounding div. + */ + FirechatUI.prototype._bindForHeightChange = function() { + var self = this, + $el = $(this._el), + lastHeight = null; + + setInterval(function() { + var height = $el.height(); + if (height != lastHeight) { + lastHeight = height; + $('.chat').each(function(i, el) { + + }); + } + }, 500); + }; + + /** + * Binds custom inner-tab events. + */ + FirechatUI.prototype._bindForTabControls = function() { + var self = this; + + // Handle click of tab close button. + $(document).delegate('[data-event="firechat-close-tab"]', 'click', function(event) { + var roomId = $(this).closest('[data-room-id]').data('room-id'); + self._chat.leaveRoom(roomId); + }); + }; + + /** + * Binds room list dropdown to populate room list on-demand. + */ + FirechatUI.prototype._bindForRoomList = function() { + var self = this; + + $('#firechat-btn-rooms').bind('click', function() { + if ($(this).parent().hasClass('open')) { + return; + } + + var $this = $(this), + template = FirechatDefaultTemplates["templates/room-list-item.html"], + selectRoomListItem = function() { + var parent = $(this).parent(), + roomId = parent.data('room-id'), + roomName = parent.data('room-name'); + + if (self.$messages[roomId]) { + self.focusTab(roomId); + } else { + self._chat.enterRoom(roomId, roomName); + } + }; + + self._chat.getRoomList(function(rooms) { + self.$roomList.empty(); + for (var roomId in rooms) { + var room = rooms[roomId]; + room.isRoomOpen = !!self.$messages[room.id]; + var $roomItem = $(template(room)); + $roomItem.children('a').bind('click', selectRoomListItem); + self.$roomList.append($roomItem.toggle(true)); + } + }); + }); + }; + + /** + * Binds user list dropdown per room to populate user list on-demand. + */ + FirechatUI.prototype._bindForUserRoomList = function() { + var self = this; + + // Upon click of the dropdown, autofocus the input field and trigger list population. + $(document).delegate('[data-event="firechat-user-room-list-btn"]', 'click', function(event) { + event.stopPropagation(); + + var $this = $(this), + roomId = $this.closest('[data-room-id]').data('room-id'), + template = FirechatDefaultTemplates["templates/room-user-list-item.html"], + targetId = $this.data('target'), + $target = $('#' + targetId); + + $target.empty(); + self._chat.getUsersByRoom(roomId, function(users) { + for (var username in users) { + user = users[username]; + user.disableActions = (user.id === self._user.id); + user.nameTrimmed = self.trimWithEllipsis(user.name, self.maxLengthUsernameDisplay); + user.isMuted = (self._user.muted && self._user.muted[user.id]); + $target.append($(template(user))); + } + self.sortListLexicographically('#' + targetId); + }); + }); + }; + + /** + * Binds user search buttons, dropdowns, and input fields for searching all + * active users currently in chat. + */ + FirechatUI.prototype._bindForUserSearch = function() { + var self = this, + handleUserSearchSubmit = function(event) { + var $this = $(this), + targetId = $this.data('target'), + controlsId = $this.data('controls'), + templateId = $this.data('template'), + prefix = $this.val() || $this.data('prefix') || '', + startAt = $this.data('startAt') || null, + endAt = $this.data('endAt') || null; + + event.preventDefault(); + + userSearch(targetId, templateId, controlsId, prefix, startAt, endAt); + }, + userSearch = function(targetId, templateId, controlsId, prefix, startAt, endAt) { + var $target = $('#' + targetId), + $controls = $('#' + controlsId), + template = FirechatDefaultTemplates[templateId]; + + // Query results, filtered by prefix, using the defined startAt and endAt markets. + self._chat.getUsersByPrefix(prefix, startAt, endAt, self.maxUserSearchResults, function(users) { + var numResults = 0, + $prevBtn, $nextBtn, username, firstResult, lastResult; + + $target.empty(); + + for (username in users) { + var user = users[username]; + + // Disable buttons for . + user.disableActions = (user.id === self._user.id); + + numResults += 1; + + $target.append(template(user)); + + // If we've hit our result limit, the additional value signifies we should paginate. + if (numResults === 1) { + firstResult = user.name.toLowerCase(); + } else if (numResults >= self.maxUserSearchResults) { + lastResult = user.name.toLowerCase(); + break; + } + } + + if ($controls) { + $prevBtn = $controls.find('[data-toggle="firechat-pagination-prev"]'); + $nextBtn = $controls.find('[data-toggle="firechat-pagination-next"]'); + + // Sort out configuration for the 'next' button + if (lastResult) { + $nextBtn + .data('event', 'firechat-user-search') + .data('startAt', lastResult) + .data('prefix', prefix) + .removeClass('disabled').removeAttr('disabled'); + } else { + $nextBtn + .data('event', null) + .data('startAt', null) + .data('prefix', null) + .addClass('disabled').attr('disabled', 'disabled'); + } + } + }); + }; + + $(document).delegate('[data-event="firechat-user-search"]', 'keyup', handleUserSearchSubmit); + $(document).delegate('[data-event="firechat-user-search"]', 'click', handleUserSearchSubmit); + + // Upon click of the dropdown, autofocus the input field and trigger list population. + $(document).delegate('[data-event="firechat-user-search-btn"]', 'click', function(event) { + event.stopPropagation(); + var $input = $(this).next('div.dropdown-menu').find('input'); + $input.focus(); + $input.trigger(jQuery.Event('keyup')); + }); + + // Ensure that the dropdown stays open despite clicking on the input element. + $(document).delegate('[data-event="firechat-user-search"]', 'click', function(event) { + event.stopPropagation(); + }); + }; + + /** + * Binds user mute toggles and removes all messages for a given user upon mute. + */ + FirechatUI.prototype._bindForUserMuting = function() { + var self = this; + $(document).delegate('[data-event="firechat-user-mute-toggle"]', 'click', function(event) { + var $this = $(this), + userId = $this.closest('[data-user-id]').data('user-id'), + userName = $this.closest('[data-user-name]').data('user-name'), + isMuted = $this.hasClass('red'), + template = FirechatDefaultTemplates["templates/prompt-user-mute.html"]; + + event.preventDefault(); + + // Require user confirmation for muting. + if (!isMuted) { + var $prompt = self.prompt('Mute User?', template({ + userName: userName + })); + + $prompt.find('a.close').first().click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=decline]').first().click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=accept]').first().click(function() { + self._chat.toggleUserMute(userId); + $prompt.remove(); + }); + } else { + self._chat.toggleUserMute(userId); + } + }); + }; + + /** + * Binds to elements with the data-event='firechat-user-(private)-invite' and + * handles invitations as well as room creation and entering. + */ + FirechatUI.prototype._bindForChatInvites = function() { + var self = this, + renderInvitePrompt = function(event) { + var $this = $(this), + userId = $this.closest('[data-user-id]').data('user-id'), + roomId = $this.closest('[data-room-id]').data('room-id'), + userName = $this.closest('[data-user-name]').data('user-name'), + template = FirechatDefaultTemplates["templates/prompt-invite-private.html"], + $prompt; + + self._chat.getRoom(roomId, function(room) { + $prompt = self.prompt('Invite', template({ + userName: userName, + roomName: room.name + })); + + $prompt.find('a.close').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=decline]').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=accept]').first().click(function() { + $prompt.remove(); + self._chat.inviteUser(userId, roomId, room.name); + }); + }); + }, + renderPrivateInvitePrompt = function(event) { + var $this = $(this), + userId = $this.closest('[data-user-id]').data('user-id'), + userName = $this.closest('[data-user-name]').data('user-name'), + template = FirechatDefaultTemplates["templates/prompt-invite-private.html"], + $prompt; + + if (userId && userName) { + $prompt = self.prompt('Private Invite', template({ + userName: userName, + roomName: 'Private Chat' + })); + + $prompt.find('a.close').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=decline]').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=accept]').first().click(function() { + $prompt.remove(); + var roomName = 'Private Chat'; + self._chat.createRoom(roomName, 'private', function(roomId) { + self._chat.inviteUser(userId, roomId, roomName); + }); + }); + } + }; + + $(document).delegate('[data-event="firechat-user-chat"]', 'click', renderPrivateInvitePrompt); + $(document).delegate('[data-event="firechat-user-invite"]', 'click', renderInvitePrompt); + }; + + /** + * Binds to room dropdown button, menu items, and create room button. + */ + FirechatUI.prototype._bindForRoomListing = function() { + var self = this, + $createRoomPromptButton = $('#firechat-btn-create-room-prompt'), + $createRoomButton = $('#firechat-btn-create-room'), + renderRoomList = function(event) { + var type = $(this).data('room-type'); + + self.sortListLexicographically('#firechat-room-list'); + }; + + // Handle click of the create new room prompt-button. + $createRoomPromptButton.bind('click', function(event) { + self.promptCreateRoom(); + }); + + // Handle click of the create new room button. + $createRoomButton.bind('click', function(event) { + var roomName = $('#firechat-input-room-name').val(); + $('#firechat-prompt-create-room').remove(); + self._chat.createRoom(roomName); + }); + }; + + /** + * A stripped-down version of bootstrap-tab.js. + * + * Original bootstrap-tab.js Copyright 2012 Twitter, Inc.,licensed under the Apache v2.0 + */ + FirechatUI.prototype._setupTabs = function() { + var self = this, + show = function($el) { + var $this = $el, + $ul = $this.closest('ul:not(.dropdown-menu)'), + selector = $this.attr('data-target'), + previous = $ul.find('.active:last a')[0], + $target, + e; + + if (!selector) { + selector = $this.attr('href'); + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); + } + + if ($this.parent('li').hasClass('active')) return; + + e = $.Event('show', { relatedTarget: previous }); + + $this.trigger(e); + + if (e.isDefaultPrevented()) return; + + $target = $(selector); + + activate($this.parent('li'), $ul); + activate($target, $target.parent(), function () { + $this.trigger({ + type: 'shown', + relatedTarget: previous + }); + }); + }, + activate = function (element, container, callback) { + var $active = container.find('> .active'), + transition = callback && $.support.transition && $active.hasClass('fade'); + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active'); + + element.addClass('active'); + + if (transition) { + element.addClass('in'); + } else { + element.removeClass('fade'); + } + + if (element.parent('.dropdown-menu')) { + element.closest('li.dropdown').addClass('active'); + } + + if (callback) { + callback(); + } + } + + if (transition) { + $active.one($.support.transition.end, next); + } else { + next(); + } + + $active.removeClass('in'); + }; + + $(document).delegate('[data-toggle="tab"]', 'click', function(event) { + event.preventDefault(); + show($(this)); + }); + }; + + /** + * A stripped-down version of bootstrap-dropdown.js. + * + * Original bootstrap-dropdown.js Copyright 2012 Twitter, Inc., licensed under the Apache v2.0 + */ + FirechatUI.prototype._setupDropdowns = function() { + var self = this, + toggle = '[data-toggle=dropdown]', + toggleDropdown = function(event) { + var $this = $(this), + $parent = getParent($this), + isActive = $parent.hasClass('open'); + + if ($this.is('.disabled, :disabled')) return; + + clearMenus(); + + if (!isActive) { + $parent.toggleClass('open'); + } + + $this.focus(); + + return false; + }, + clearMenus = function() { + $('[data-toggle=dropdown]').each(function() { + getParent($(this)).removeClass('open'); + }); + }, + getParent = function($this) { + var selector = $this.attr('data-target'), + $parent; + + if (!selector) { + selector = $this.attr('href'); + selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); + } + + $parent = selector && $(selector); + + if (!$parent || !$parent.length) $parent = $this.parent(); + + return $parent; + }; + + $(document) + .bind('click', clearMenus) + .delegate('.dropdown-menu', 'click', function(event) { event.stopPropagation(); }) + .delegate('[data-toggle=dropdown]', 'click', toggleDropdown); + }; + + /** + * Binds to any text input fields with data-provide='limit' and + * data-counter='', and upon value change updates the selector + * content to reflect the number of characters remaining, as the 'maxlength' + * attribute less the current value length. + */ + FirechatUI.prototype._bindTextInputFieldLimits = function() { + $('body').delegate('input[data-provide="limit"], textarea[data-provide="limit"]', 'keyup', function(event) { + var $this = $(this), + $target = $($this.data('counter')), + limit = $this.attr('maxlength'), + count = $this.val().length; + + $target.html(Math.max(0, limit - count)); + }); + }; + + /** + * Given a title and message content, show an alert prompt to the user. + * + * @param {string} title + * @param {string} message + */ + FirechatUI.prototype.renderAlertPrompt = function(title, message) { + var template = FirechatDefaultTemplates["templates/prompt-alert.html"], + $prompt = this.prompt(title, template({ message: message })); + + $prompt.find('.close').click(function() { + $prompt.remove(); + }); + return; + }; + + /** + * Toggle input field s if we want limit / unlimit input fields. + */ + FirechatUI.prototype.toggleInputs = function(isEnabled) { + $('#firechat-tab-content textarea').each(function() { + var $this = $(this); + if (isEnabled) { + $(this).val(''); + } else { + $(this).val('You have exceeded the message limit, please wait before sending.'); + } + $this.prop('disabled', !isEnabled); + }); + $('#firechat-input-name').prop('disabled', !isEnabled); + }; + + /** + * Given a room id and name, attach the tab to the interface and setup events. + * + * @param {string} roomId + * @param {string} roomName + */ + FirechatUI.prototype.attachTab = function(roomId, roomName) { + var self = this; + + // If this tab already exists, give it focus. + if (this.$messages[roomId]) { + this.focusTab(roomId); + return; + } + + var room = { + id: roomId, + name: roomName + }; + + // Populate and render the tab content template. + var tabTemplate = FirechatDefaultTemplates["templates/tab-content.html"]; + var $tabContent = $(tabTemplate(room)); + this.$tabContent.prepend($tabContent); + var $messages = $('#firechat-messages' + roomId); + + // Keep a reference to the message listing for later use. + this.$messages[roomId] = $messages; + + // Attach on-enter event to textarea. + var $textarea = $tabContent.find('textarea').first(); + $textarea.bind('keydown', function(e) { + var message = self.trimWithEllipsis($textarea.val(), self.maxLengthMessage); + if ((e.which === 13) && (message !== '')) { + $textarea.val(''); + self._chat.sendMessage(roomId, message); + return false; + } + }); + + // Populate and render the tab menu template. + var tabListTemplate = FirechatDefaultTemplates["templates/tab-menu-item.html"]; + var $tab = $(tabListTemplate(room)); + this.$tabList.prepend($tab); + + // Attach on-shown event to move tab to front and scroll to bottom. + $tab.bind('shown', function(event) { + $messages.scrollTop($messages[0].scrollHeight); + }); + + // Dynamically update the width of each tab based upon the number open. + var tabs = this.$tabList.children('li'); + var tabWidth = Math.floor($('#firechat-tab-list').width() / tabs.length); + this.$tabList.children('li').css('width', tabWidth); + + // Update the room listing to reflect that we're now in the room. + this.$roomList.children('[data-room-id=' + roomId + ']').children('a').addClass('highlight'); + + // Sort each item in the user list alphabetically on click of the dropdown. + $('#firechat-btn-room-user-list-' + roomId).bind('click', function() { + self.sortListLexicographically('#firechat-room-user-list-' + roomId); + }); + + // Automatically select the new tab. + this.focusTab(roomId); + }; + + /** + * Given a room id, focus the given tab. + * + * @param {string} roomId + */ + FirechatUI.prototype.focusTab = function(roomId) { + if (this.$messages[roomId]) { + var $tabLink = this.$tabList.find('[data-room-id=' + roomId + ']').find('a'); + if ($tabLink.length) { + $tabLink.first().trigger('click'); + } + } + }; + + /** + * Given a room id, remove the tab and all child elements from the interface. + * + * @param {string} roomId + */ + FirechatUI.prototype.removeTab = function(roomId) { + delete this.$messages[roomId]; + + // Remove the inner tab content. + this.$tabContent.find('[data-room-id=' + roomId + ']').remove(); + + // Remove the tab from the navigation menu. + this.$tabList.find('[data-room-id=' + roomId + ']').remove(); + + // Dynamically update the width of each tab based upon the number open. + var tabs = this.$tabList.children('li'); + var tabWidth = Math.floor($('#firechat-tab-list').width() / tabs.length); + this.$tabList.children('li').css('width', tabWidth); + + // Automatically select the next tab if there is one. + this.$tabList.find('[data-toggle=tab]').first().trigger('click'); + + // Update the room listing to reflect that we're now in the room. + this.$roomList.children('[data-room-id=' + roomId + ']').children('a').removeClass('highlight'); + }; + + /** + * Render a new message in the specified chat room. + * + * @param {string} roomId + * @param {string} message + */ + FirechatUI.prototype.showMessage = function(roomId, rawMessage) { + var self = this; + + // Setup defaults + var message = { + id : rawMessage.id, + localtime : this.formatTime(rawMessage.timestamp), + message : rawMessage.message || '', + userId : rawMessage.userId, + name : rawMessage.name, + type : rawMessage.type || 'default', + isSelfMessage : (rawMessage.userId == this._user.id) + }; + + // While other data is escaped in the Underscore.js templates, escape and + // process the message content here to add additional functionality (add links). + // Also trim the message length to some client-defined maximum. + var messageConstructed = ''; + message.message = _.map(message.message.split(' '), function(token) { + if (self.urlPattern.test(token) || self.pseudoUrlPattern.test(token)) { + return self.linkify(encodeURI(token)); + } else { + return _.escape(token); + } + }).join(' '); + message.message = this.trimWithEllipsis(message.message, this.maxLengthMessage); + + // Populate and render the message template. + var template = FirechatDefaultTemplates["templates/message.html"]; + var $message = $(template(message)); + var $messages = this.$messages[roomId]; + if ($messages) { + + var scrollToBottom = false; + if ($messages.scrollTop() / ($messages[0].scrollHeight - $messages[0].offsetHeight) >= 0.95) { + // Pinned to bottom + scrollToBottom = true; + } else if ($messages[0].scrollHeight <= $messages.height()) { + // Haven't added the scrollbar yet + scrollToBottom = true; + } + + $messages.append($message); + + if (scrollToBottom) { + $messages.scrollTop($messages[0].scrollHeight); + } + } + }; + + /** + * Remove a message by id. + * + * @param {string} roomId + * @param {string} messageId + */ + FirechatUI.prototype.removeMessage = function(roomId, messageId) { + $('.message[data-message-id="' + messageId + '"]').remove(); + }; + + /** + * Given a selector for a list element, sort the items alphabetically. + * + * @param {string} selector + */ + FirechatUI.prototype.sortListLexicographically = function(selector) { + $(selector).children("li").sort(function(a, b) { + var upA = $(a).text().toUpperCase(); + var upB = $(b).text().toUpperCase(); + return (upA < upB) ? -1 : (upA > upB) ? 1 : 0; + }).appendTo(selector); + }; + + /** + * Remove leading and trailing whitespace from a string and shrink it, with + * added ellipsis, if it exceeds a specified length. + * + * @param {string} str + * @param {number} length + * @return {string} + */ + FirechatUI.prototype.trimWithEllipsis = function(str, length) { + str = str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + return (length && str.length <= length) ? str : str.substring(0, length) + '...'; + }; + + /** + * Given a timestamp, format it in the form hh:mm am/pm. Defaults to now + * if the timestamp is undefined. + * + * @param {Number} timestamp + * @param {string} date + */ + FirechatUI.prototype.formatTime = function(timestamp) { + var date = (timestamp) ? new Date(timestamp) : new Date(), + hours = date.getHours() || 12, + minutes = '' + date.getMinutes(), + ampm = (date.getHours() >= 12) ? 'pm' : 'am'; + + hours = (hours > 12) ? hours - 12 : hours; + minutes = (minutes.length < 2) ? '0' + minutes : minutes; + return '' + hours + ':' + minutes + ampm; + }; + + /** + * Launch a prompt to allow the user to create a new room. + */ + FirechatUI.prototype.promptCreateRoom = function() { + var self = this; + var template = FirechatDefaultTemplates["templates/prompt-create-room.html"]; + + var $prompt = this.prompt('Create Public Room', template({ + maxLengthRoomName: this.maxLengthRoomName, + isModerator: self._chat.userIsModerator() + })); + $prompt.find('a.close').first().click(function() { + $prompt.remove(); + }); + + + $prompt.find('[data-toggle=submit]').first().click(function() { + var name = $prompt.find('[data-input=firechat-room-name]').first().val(); + if (name !== '') { + self._chat.createRoom(name, 'public'); + $prompt.remove(); + } + }); + + $prompt.find('[data-input=firechat-room-name]').first().focus(); + $prompt.find('[data-input=firechat-room-name]').first().bind('keydown', function(e) { + if (e.which === 13) { + var name = $prompt.find('[data-input=firechat-room-name]').first().val(); + if (name !== '') { + self._chat.createRoom(name, 'public'); + $prompt.remove(); + return false; + } + } + }); + }; + + /** + * Inner method to launch a prompt given a specific title and HTML content. + * @param {string} title + * @param {string} content + */ + FirechatUI.prototype.prompt = function(title, content) { + var template = FirechatDefaultTemplates["templates/prompt.html"], + $prompt; + + $prompt = $(template({ + title: title, + content: content + })).css({ + top: this.$wrapper.position().top + (0.333 * this.$wrapper.height()), + left: this.$wrapper.position().left + (0.125 * this.$wrapper.width()), + width: 0.75 * this.$wrapper.width() + }); + this.$wrapper.append($prompt.removeClass('hidden')); + return $prompt; + }; + + // see http://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links + FirechatUI.prototype.linkify = function(str) { + var self = this; + return str + .replace(self.urlPattern, '$&') + .replace(self.pseudoUrlPattern, '$1$2'); + }; + +})(jQuery); diff --git a/firechat/0.1.1/firechat-default.min.css b/firechat/0.1.1/firechat-default.min.css new file mode 100644 index 0000000..34b54b6 --- /dev/null +++ b/firechat/0.1.1/firechat-default.min.css @@ -0,0 +1 @@ +@charset "UTF-8";#firechat div,#firechat span,#firechat applet,#firechat object,#firechat iframe,#firechat h1,#firechat h2,#firechat h3,#firechat h4,#firechat h5,#firechat h6,#firechat p,#firechat blockquote,#firechat pre,#firechat a,#firechat abbr,#firechat acronym,#firechat address,#firechat big,#firechat cite,#firechat code,#firechat del,#firechat dfn,#firechat em,#firechat img,#firechat ins,#firechat kbd,#firechat q,#firechat s,#firechat samp,#firechat small,#firechat strike,#firechat strong,#firechat sub,#firechat sup,#firechat tt,#firechat var,#firechat b,#firechat u,#firechat i,#firechat center,#firechat dl,#firechat dt,#firechat dd,#firechat ol,#firechat ul,#firechat li,#firechat fieldset,#firechat form,#firechat label,#firechat legend,#firechat table,#firechat caption,#firechat tbody,#firechat tfoot,#firechat thead,#firechat tr,#firechat th,#firechat td,#firechat article,#firechat aside,#firechat canvas,#firechat details,#firechat embed,#firechat figure,#firechat figcaption,#firechat footer,#firechat header,#firechat hgroup,#firechat menu,#firechat nav,#firechat output,#firechat ruby,#firechat section,#firechat summary,#firechat time,#firechat mark,#firechat audio,#firechat video{border:0;font-size:12px;font-family:arial,helvetica,sans-serif;vertical-align:baseline;margin:0;padding:0}#firechat article,#firechat aside,#firechat details,#firechat figcaption,#firechat figure,#firechat footer,#firechat header,#firechat hgroup,#firechat menu,#firechat nav,#firechat section{display:block}#firechat body{line-height:1}#firechat ol,#firechat ul{list-style:none}#firechat blockquote,#firechat q{quotes:none}#firechat blockquote:before,#firechat blockquote:after,#firechat q:before,#firechat q:after{content:none}#firechat table{border-collapse:collapse;border-spacing:0}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}.clearfix:after{clear:both}#firechat{color:#333;text-align:left}#firechat .center{float:none!important;margin-left:auto!important;margin-right:auto!important}#firechat .left{float:left!important}#firechat .right{float:right!important}#firechat .alignleft{text-align:left!important}#firechat .alignright{text-align:right!important}#firechat .aligncenter{text-align:center!important}#firechat .hidden{display:none!important}#firechat .row{clear:both}#firechat .fifth,#firechat .fivesixth,#firechat .fourfifth,#firechat .half,#firechat .ninetenth,#firechat .quarter,#firechat .sevententh,#firechat .sixth,#firechat .tenth,#firechat .third,#firechat .threefifth,#firechat .threequarter,#firechat .threetenth,#firechat .twofifth,#firechat .twothird,#firechat .full{margin-left:2.127659574468085%;float:left;min-height:1px}#firechat .fifth:first-child,#firechat .fivesixth:first-child,#firechat .fourfifth:first-child,#firechat .half:first-child,#firechat .ninetenth:first-child,#firechat .quarter:first-child,#firechat .sevententh:first-child,#firechat .sixth:first-child,#firechat .tenth:first-child,#firechat .third:first-child,#firechat .threefifth:first-child,#firechat .threequarter:first-child,#firechat .threetenth:first-child,#firechat .twofifth:first-child,#firechat .twothird:first-child,#firechat .full:first-child{margin-left:0}#firechat .tenth{width:8.08510638297872%}#firechat .sixth{width:14.893617021276595%}#firechat .fifth{width:18.297872340425535%}#firechat .quarter{width:23.404255319148938%}#firechat .threetenth{width:26.3829787235%}#firechat .third{width:31.914893617021278%}#firechat .twofifth{width:38.72340425531915%}#firechat .half{width:48.93617021276596%}#firechat .sevententh{width:58.7234042555%}#firechat .threefifth{width:59.14893617021278%}#firechat .twothird{width:65.95744680851064%}#firechat .threequarter{width:74.46808510638297%}#firechat .ninetenth{width:74.8936170215%}#firechat .fourfifth{width:79.57446808510639%}#firechat .fivesixth{width:82.9787234042553%}#firechat .full{width:100%}#firechat .clipped{overflow:hidden}#firechat strong{font-weight:bold}#firechat em{font-style:italic}#firechat label{display:block}#firechat a{color:#005580}#firechat a:visited,#firechat a:hover,#firechat a:active{color:#005580}#firechat p{margin:10px 0}#firechat h1,#firechat h2,#firechat h3,#firechat h4,#firechat h5,#firechat h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit}#firechat h1,#firechat h2,#firechat h3{line-height:40px}#firechat h1{font-size:38.5px}#firechat h2{font-size:31.5px}#firechat h3{font-size:24.5px}#firechat h4{font-size:17.5px}#firechat h5{font-size:14px}#firechat h6{font-size:11.9px}#firechat small{font-size:90%}#firechat .nav{list-style:none}#firechat .nav>li>a{display:block;background-color:#eee;text-decoration:none;overflow:hidden;white-space:nowrap}#firechat .nav>li>a:hover,#firechat .nav>li>a:focus{background-color:#fff}#firechat .nav-tabs{border-bottom:1px solid #ddd;clear:both}#firechat .nav-tabs>li{float:left;margin-bottom:-1px;max-width:45%}#firechat .nav-tabs>li>a{-webkit-border-top-right-radius:4px;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:4px;padding:4px 8px;margin-right:2px;line-height:20px;border:1px solid transparent;border-color:#ccc}#firechat .nav-tabs>.active>a,#firechat .nav-tabs>.active>a:hover,#firechat .nav-tabs>.active>a:focus{border-bottom-color:transparent;background-color:#fff;cursor:default}#firechat .tab-content{overflow:auto}#firechat .tab-content>.tab-pane{display:none}#firechat .tab-content>.active{display:block;background-color:#fff}#firechat .caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";margin-top:8px;margin-left:2px}#firechat .dropdown{position:relative}#firechat .dropdown-toggle{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none}#firechat .dropdown-toggle:focus,#firechat .dropdown-toggle:active{outline:0;text-decoration:none}#firechat .dropdown-toggle.btn{padding:4px 0 0;height:22px}#firechat .dropdown-menu{*zoom:1;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top-left-radius:0;z-index:1000;display:none;float:left;position:absolute;top:100%;left:0;width:100%;background-color:#fff;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;min-width:98%;padding:0;margin:-1px 0 0}#firechat .dropdown-menu:before,#firechat .dropdown-menu:after{display:table;content:"";line-height:0}#firechat .dropdown-menu:after{clear:both}#firechat .dropdown-menu ul{background-color:#fff;list-style:none;overflow-y:scroll;max-height:300px}#firechat .dropdown-menu ul>li>a{display:block;padding:1px 1px 1px 3px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}#firechat .dropdown-menu ul>li>a.highlight{background-color:#d9edf7}#firechat .dropdown-menu ul>li>a:hover,#firechat .dropdown-menu ul>li>a:focus,#firechat .dropdown-menu ul>.active>a,#firechat .dropdown-menu ul>.active>a:hover,#firechat .dropdown-menu ul>.active>a:focus{text-decoration:none;color:#000;background-color:#d9edf7;outline:0}#firechat .dropdown-menu ul>.disabled>a,#firechat .dropdown-menu ul>.disabled>a:hover,#firechat .dropdown-menu ul>.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;background-image:none;cursor:default}#firechat .dropdown-header{position:relative;width:100%;padding:10px 0;background-color:#eee;border-bottom:1px solid #ccc}#firechat .dropdown-footer{position:relative;width:100%;padding:10px 0;background-color:#eee;border-top:1px solid #ccc;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#firechat .open{*z-index:1000}#firechat .open>.dropdown-menu{display:block;border:1px solid #ccc;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top-left-radius:0}#firechat .open>.dropdown-toggle{outline:0;text-decoration:none;-webkit-border-top-right-radius:4px;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:4px}#firechat .prompt-wrapper{position:absolute;z-index:1000}#firechat .prompt{position:absolute;z-index:1001;background-color:#fff;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.45);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.45);box-shadow:0 5px 10px rgba(0,0,0,0.45)}#firechat .prompt-header{padding:4px 8px;font-weight:bold;background-color:#eee;border:1px solid #ccc;-webkit-border-top-right-radius:4px;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:4px}#firechat .prompt-header a.close{opacity:.6;font-size:13px;margin-top:2px}#firechat .prompt-header a.close:hover{opacity:.9}#firechat .prompt-body{background-color:#fff;padding:4px 8px;border-left:1px solid #ccc;border-right:1px solid #ccc}#firechat .prompt-footer{padding:4px 8px;background-color:#eee;border:1px solid #ccc;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top-left-radius:0}#firechat .prompt-background{background-color:#333;border:1px solid #333;opacity:.8;z-index:1000;height:100%;width:100%}#firechat .btn{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;height:24px;display:inline-block;*display:inline;*zoom:1;padding:2px 5px;margin-bottom:0;text-align:center;vertical-align:middle;cursor:pointer;color:#333;font-size:12px;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}#firechat .btn:hover,#firechat .btn:focus,#firechat .btn:active,#firechat .btn.active,#firechat .btn.disabled,#firechat .btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9;outline:0}#firechat .btn:active,#firechat .btn.active{background-color:#ccc}#firechat .btn:first-child{*margin-left:0}#firechat .btn:hover,#firechat .btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}#firechat .btn.active,#firechat .btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}#firechat .btn.disabled,#firechat .btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65)}#firechat .btn.disabled:active,#firechat .btn[disabled]:active{-webkit-box-shadow:inherit;-moz-box-shadow:inherit;box-shadow:inherit;background-color:#e6e6e6}#firechat .contextmenu{position:fixed;z-index:1001;min-width:150px;border:1px solid #ccc;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}#firechat .contextmenu ul{background-color:#fff;list-style:none}#firechat .contextmenu ul>li>a{display:block;padding:3px 10px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}#firechat .contextmenu ul>li>a.highlight{background-color:#d9edf7}#firechat .contextmenu ul>li>a:hover,#firechat .contextmenu ul>li>a:focus{text-decoration:none;color:#fff;background-color:#0081c2;outline:0}#firechat{padding:0;font-family:sans-serif;font-size:12px;line-height:18px}#firechat input,#firechat textarea{width:100%;font-family:sans-serif;font-size:12px;line-height:18px;padding:2px 5px;border:1px solid #ccc;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#firechat input:-moz-placeholder,#firechat textarea:-moz-placeholder{color:#aaa}#firechat input:-ms-input-placeholder,#firechat textarea:-ms-input-placeholder{color:#aaa}#firechat input::-webkit-input-placeholder,#firechat textarea::-webkit-input-placeholder{color:#aaa}#firechat input[disabled],#firechat textarea[disabled]{background-color:#eee}#firechat input{height:24px}#firechat textarea{resize:none;height:40px}#firechat .search-wrapper{-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;border:1px solid #ccc;margin:0 5px;padding:2px 5px;background:#fff}#firechat .search-wrapper>input[type=text]{padding-left:0;border:0}#firechat .search-wrapper>input[type=text]:focus,#firechat .search-wrapper>input[type=text]:active{outline:0}#firechat .chat{overflow:auto;-ms-overflow-x:hidden;overflow-x:hidden;height:290px;position:relative;margin-bottom:5px;border:1px solid #ccc;border-top:0;overflow-y:scroll}#firechat .chat textarea{overflow:auto;vertical-align:top}#firechat .message{color:#333;padding:3px 5px;border-bottom:1px solid #ccc}#firechat .message.highlighted{background-color:#d9edf7}#firechat .message .name{font-weight:bold;overflow-x:hidden}#firechat .message.message-self{color:#2675ab}#firechat .message:nth-child(odd){background-color:#f9f9f9}#firechat .message:nth-child(odd).highlighted{background-color:#d9edf7}#firechat .message:nth-child(odd).message-local{background-color:#effafc}#firechat .message-content{word-wrap:break-word;padding-right:45px}#firechat .message-content.red{color:red}#firechat .message.message-notification .message-content{font-style:italic}#firechat ul::-webkit-scrollbar{-webkit-appearance:none;width:7px}#firechat ul::-webkit-scrollbar-thumb{border-radius:4px;-webkit-box-shadow:0 0 1px rgba(255,255,255,0.5)}#firechat #firechat-header{padding:6px 0 0 0;height:40px}#firechat #firechat-tabs{height:435px}#firechat #firechat-tab-list{background-color:#fff}#firechat #firechat-tab-content{width:100%;background-color:#fff}#firechat .tab-pane-menu{border:1px solid #ccc;border-top:0;vertical-align:middle;padding-bottom:5px}#firechat .tab-pane-menu .dropdown{margin:5px 0 0 5px}#firechat .tab-pane-menu>.icon{margin:5px 2px 0}#firechat .icon{display:inline-block;*margin-right:.3em;line-height:20px;vertical-align:middle;background-repeat:no-repeat;padding:0;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL8AAAANEAYAAACoeGM7AAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAM80lEQVR42t2cZ4xWRRfHZ3dhwbK7CJZYYiOxV+wlUQzCKmBBsWPvYjQiaiyxYUwULLErltgrNpRF4q5iV0BRLFE0diUi7LIq6Pqw74f//rzO4Zm9d+5z9c37ni//3OfemXtm5sypc58q55xzrrpauHSpcOONheedJ9x9d2Hv3sLvvxfefbfw5puFf/5Zvr+8VFUl7Ozs/rkNNxTef79w222F77wjPOoo4ccfF/O+UDvGzTVEf8wH1zU1wlJJePTRwv33Fx57rPDnn/1+wbzzG+IXvrhmPUPzY/kJEc/HzqslK1fLLy8cMUK4667CFVYQ/vqr8PXXhU884f8eL6eXXKJ2wqSd3tDY2KOHc84NGqSRnnCC7tbVCdvbhRMnasWnTdt5Z+eca2pK6797Yh6ef1641lrCPfYQfv21UNwl62qv115b2Nws/PZb4d57C3/7LftalaOQ/C63nPCII4Tsg6228scHffqp8LHHhPfe6/8eu49feEH44YfCq64S/v67cMGC7tuvu65w6FDhllsKkS/0zoQJaZzMmSO+JaEzZmgkY8b88Ydzzs2b19HhnHNffSWJ6tVLM1cq/fKLrjXiUaPUrn9/XZ9yip6rqVmyxDnnnnrKvPbAA4V6TTJxP/zgT4zdyC+9JOzXz++PgceSVSS1tUJtE+c22si/f+21Pj9Wwd54o//8ZpsJd9xR2KtX9+8vmkLzcsABPt9z5wr32af88xiOf4oaGoSHH17M+/LKg203aJDwiy+EVh5DyHzuuWcxfDn31lvOOffMM7Nn6z1SsZ2dn30m/OQT4eefC7/8UvjBB0LaVzav7D873vfeE6LQISvv3Of50P6OJetgWFp5ZeFzz5V/b1bEEd1333x80g+O4mWXCXEQMDB33CG8/XYhBhuDmMZnOn30kZ5/+23nnGtpefNN55wbP96Xl/nzhYsWCdvauEbeQP1Of6US/XRZfDz8Bx8U9uwpPPFE4eOPC/EMVl9dOG6ccORI4Q03CA87LHbA5RcCwgO47TbhokXCYcOERCKQ7GJiMLiPokeQ2QCjRwtvukkY6zHg0V10kXDAAH++EIz77hM++aRwxRWFq64q3H57v9/+/YVPPy2cOFF47rnCNE8kll/Gi4eIAWU9v/vOnz8MA4qjb18hG51+2JiLF+ebX/hA4U+b5t+/7jrhAw8IkQ88WvnPiZzi4Q0eXL6/dEJhawaGDydmk4uEQmhu1kj79tVImb8RI8TZwIG0p78ddnAubOhDhIcPIXd4nnh4OHYYzPXXF7K/ed5GBLb/NLKRFOtMfyCZAiIKns8aSVp9xP4i8nv//Ti+icjgl0iDeUsjpKDSyJYeqqs1A+3taDBpkn79Qi5YW9vf5wXq0YP+1L5U6loAUjpW4WPhQhN00EFC2SXnDj1UiAF44w2hTWnEEqkcCIWJYkShQNbDIIJZZx2h9Xhs/7EpFDwCNs6UKf584tGQsiFCGTJEuMEG/jxBzBe/H3+8cJddhBiAZ58thl/eM2uW8MgjhWyAyy8XojhRUGuuKayvF7Jhf/pJ+O67wltvFU6dGscv78fTgg45RPjII+XbYZA23bT8/Vtu8ecBDy9MpHS0EROFr1B76lQp7r32su20EwYMkGSOG4dq4I0a4fDh9G9TQN0TKR0iB8aD3G+9tRDHDoPPenKf53GYiABiPWm7f046SUgKDs8aQwxlVfiQTVXh0BChxir+kDol5WPv24im+Ahcb6iu9jVnR4euGX9oXsLj7GKYHP68eUI8ANsRA7WKE8sNkUopdvgJsW0QUBbc3oewl6GQs9LUDgr85JOF5OZHjRKyESZNEp55ppBIC7IbxgoSkQzt2OgXX1wMv9RCFBAuG4HgSREKU0vB42Jjo6gxtPvtJ8TzTM91+kQOf731hHj4KHybA0bRK8B1bpNNhAqkE8OH40CKLZ3I4a+0kq6l8JuapPAbGx991Lm/r5s8+aFDlWOdOVMuS0ODNm5bm1RWSwsSrJY2FZVGRFYYYgwt+wO5EpeJ4eaa+zxPexQ+/Wcl+iFCxeBvs43QRujW048lu09Y16KI/m3Ewu8h/VS55w/5b7AGMrbW5lyXImQh8EhtqGBDGOu525QDnm5RhOcBzZ8vfO01IYIaev/Mmf5zpALsfSiv5bYLfeqpQhSUrYHY92GYbD+shx0XKSOKlrFk1xHFTQRon7M1FNtepaOErwsvFBIqkzKKlQ8MDoTnCpFKwyCi2FZbTYhDgydO6gfaaaesnFC0xQcUUQwlBi6VpPCHDJGKmDyZGWMldY2nPmmSP5NEdlkJRYSCZr7FRbiIzTX3eZ72tiiclZjf444rzyd6hkid9+fNCNhxvfJKvn5C/YVy9nZVYw89ZCdfI6TVEuCLCI4Zhp+Ojq6FIAdLFR0PjpQOFpyFsQMidw5ZxRo7ATaEI6fMgFD8FJsxXCgee00qSiWPJNfKaYLp08u/P1YQ4ZfxWgWDAcOjZmMR8G+3nZBTPbwffhYuFJ5zjhCPKi9ZBUzkh8dpBTkk2KSKLr1UiAIJEYYhK2GQIOto4OGj8NdYQ4hck8tnvkmtQSGDHKK6On8nUNtIPHwUvp05SeSwYTI1s2YpJhk5kliO/uP4Qe6IxMnhk9JhXqyCtQ4Oz9Oe/nwzl05EtJbI5fMeTs8QscIP+zwtEuA+csz+fvjhOH5tf6SSoZCjEipeKw5MVj12PemZ9tXVvunt2dOuHE+K06oqvbG2lp2ikS1ZIknv3burO45lXn+9kKItOfzQwhO6jR0r5PiXPaUQmzNHkMllIyhMMBudmgKnjqxnwnSssooQhWpTLOR6UVwc/4ul1lYhx7rwRBF4PBEEwxLFavhkeTmmN2aMEAG3ghdrqHx149z555efx9DGY37vvFNoPcxQe1v0SyObe8cRIYf/4otCPHwUPsVge4yXWgTEumWl9nZtsPp6irbk8OVyTJ5sYyRJdGOjkitJjUP3FyzwNzIOV1ZivdinFG3J4dt5twqW9eJ5nUFKDIB1xNIIh8p6oijUa64RcnyT2SLSJHWXdtqKcdD/BRcIifBiCf44TIGe2WILYaiGhWHFoaFGxnxtvnlWDvwZY/yLF6toy+8LF2rGamrESXK8WRIwe7YcjKYmzdD06Xpi7lzFWqR4/1pYeywMT5ncL4rpiiuE9tgnIRyeozP9ZyVSBNZyskHxpFFUCGroOCf36Zf28MtzRCqxOXPaM24iJxsJQQgOCgyiqM4GJvduKZRbjOWXXH+fPkKKiUQmOnyYRBrwZeeXCIxxQ/mPSfpEkZn3KZO+7PHiH38U2mKurQHY478ooHSSaZswgWOZum5uFra2iqPOTs0g93EE4NzWAKZM4RioIoDx4/PNE4YQxWNTs6HUgL1Pe3sMNCuF9qOVG2penIPHYOHokcJ8+WVhS4sQR4NaJP3F7luI9tSALBFxovhBW6uonFSORm7mzMEAzJjhnHMNDdLI9fXgq68651xdnZLYffpY+cpIhLzkUNNySd98I7QKFPs0cGC+4dv3sCAYIE4bhXJboWvwrruExxwjJPWStyjD8/b0AyEiCt4Wh6xBpCi+225CG5kUdWogxC/HMyldshGJ7IhcWIfTTxciLxhWqKgcJxuPc/h2nTAAaYrq4IP9dni01jCEiVM3nIdWdaizUxuTDdvaSgQQ6kfPNTVhKPgOgP7j5id0Dh9DbR05Unlch54PfQeQRuyrUM7ZOoxkCq6+WkikhiOBIQ/JEx7sGWfE8QlZOYKyOi7sS+uQgdn3ra/4LT/ZSS1razEEfBgoOf0rdRUaIB4rXyCedpqQFAu5KzxdPyBJPPRlj7d1Tw89JLznHiGeb+hDFVIWIQPll9SS3ynKUUwj5RWbI6Q/inWVEgJsUxRFKdKi+T3rLKEtshf9IZytPdgIgNM75PA5dWQ9fBAFE0+cu+dDGQyAHwGMHi0P/sordT12LB4+Ct//0Cbvh1zWINr9gAePIYe4thGCbU//sYRHn/fDLBwyawhDOfe8ESbvCx3/zHuKJp5Cil8cVlVlxcjXhv5yICvh4aPwreDlJY5nUay1Cj2rINn2lD4qPX6KwcPzRQHhKeP5WeQ+ng2nSyhCcv4fKip1EuIXRQlfXIMUn7nPl9SkhKhFFM2v7QcDEIoAQoiHbxV+5V/uEgGQAuJLXRQ7X/KS0sHD///8cpcIl/P69MN3GDh2RBzUvvjLFVI2NuK1einty+CsZDMY/z0qyuOvkGwIY8+zMuE2ZYGHj8JHUWQlm/sm1ENRZ81dWrShLEVAy1/20F9EEZrTQnk9HJCNYL+YLMrD+F/jF7IbnBQQNQDOi/MFJ9fk8O26xisMQmb7OykaagD+p/RLl3JNDj+U0gn13z0xLhQ0BtEqfLtP7TXP057+YvcDCpvDInwvYInUcnIqqjLKK2/IydlnF8NHfvq3FP9/ANgTCJ9z9ZF7AAAAAElFTkSuQmCC) no-repeat top left;opacity:.3;font-size:22px;font-family:Arial;font-weight:bold;overflow:hidden}#firechat .icon.plus{margin-top:0;vertical-align:top;background:transparent}#firechat .icon.search{background-position:0 0;width:13px;height:13px}#firechat .icon.close{background-position:-120px 0;width:13px;height:13px}#firechat .icon.user-chat{background-position:-138px 0;width:17px;height:13px}#firechat .icon.user-group{background-position:-18px 0;width:17px;height:13px}#firechat .icon.user-mute{background-position:-84px 0;width:13px;height:13px}#firechat .icon.user-mute.red{background-position:-102px 0;width:13px;height:13px}#firechat .icon:hover,#firechat .btn:hover>.icon{opacity:.6}#firechat a>.icon{margin:3px 1px} \ No newline at end of file diff --git a/firechat/0.1.1/firechat-default.min.js b/firechat/0.1.1/firechat-default.min.js new file mode 100644 index 0000000..fd050ed --- /dev/null +++ b/firechat/0.1.1/firechat-default.min.js @@ -0,0 +1,2 @@ +(function(){var e=this,t=e._,n={},a=Array.prototype,i=Object.prototype,r=Function.prototype,s=a.push,o=a.slice,c=a.concat,l=i.toString,u=i.hasOwnProperty,d=a.forEach,h=a.map,p=a.reduce,f=a.reduceRight,m=a.filter,v=a.every,_=a.some,g=a.indexOf,b=a.lastIndexOf,y=Array.isArray,w=Object.keys,x=r.bind,R=function(e){return e instanceof R?e:this instanceof R?(this._wrapped=e,void 0):new R(e)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=R),exports._=R):e._=R,R.VERSION="1.4.4";var j=R.each=R.forEach=function(e,t,a){if(null!=e)if(d&&e.forEach===d)e.forEach(t,a);else if(e.length===+e.length){for(var i=0,r=e.length;r>i;i++)if(t.call(a,e[i],i,e)===n)return}else for(var s in e)if(R.has(e,s)&&t.call(a,e[s],s,e)===n)return};R.map=R.collect=function(e,t,n){var a=[];return null==e?a:h&&e.map===h?e.map(t,n):(j(e,function(e,i,r){a[a.length]=t.call(n,e,i,r)}),a)};var I="Reduce of empty array with no initial value";R.reduce=R.foldl=R.inject=function(e,t,n,a){var i=arguments.length>2;if(null==e&&(e=[]),p&&e.reduce===p)return a&&(t=R.bind(t,a)),i?e.reduce(t,n):e.reduce(t);if(j(e,function(e,r,s){i?n=t.call(a,n,e,r,s):(n=e,i=!0)}),!i)throw new TypeError(I);return n},R.reduceRight=R.foldr=function(e,t,n,a){var i=arguments.length>2;if(null==e&&(e=[]),f&&e.reduceRight===f)return a&&(t=R.bind(t,a)),i?e.reduceRight(t,n):e.reduceRight(t);var r=e.length;if(r!==+r){var s=R.keys(e);r=s.length}if(j(e,function(o,c,l){c=s?s[--r]:--r,i?n=t.call(a,n,e[c],c,l):(n=e[c],i=!0)}),!i)throw new TypeError(I);return n},R.find=R.detect=function(e,t,n){var a;return k(e,function(e,i,r){return t.call(n,e,i,r)?(a=e,!0):void 0}),a},R.filter=R.select=function(e,t,n){var a=[];return null==e?a:m&&e.filter===m?e.filter(t,n):(j(e,function(e,i,r){t.call(n,e,i,r)&&(a[a.length]=e)}),a)},R.reject=function(e,t,n){return R.filter(e,function(e,a,i){return!t.call(n,e,a,i)},n)},R.every=R.all=function(e,t,a){t||(t=R.identity);var i=!0;return null==e?i:v&&e.every===v?e.every(t,a):(j(e,function(e,r,s){return(i=i&&t.call(a,e,r,s))?void 0:n}),!!i)};var k=R.some=R.any=function(e,t,a){t||(t=R.identity);var i=!1;return null==e?i:_&&e.some===_?e.some(t,a):(j(e,function(e,r,s){return i||(i=t.call(a,e,r,s))?n:void 0}),!!i)};R.contains=R.include=function(e,t){return null==e?!1:g&&e.indexOf===g?-1!=e.indexOf(t):k(e,function(e){return e===t})},R.invoke=function(e,t){var n=o.call(arguments,2),a=R.isFunction(t);return R.map(e,function(e){return(a?t:e[t]).apply(e,n)})},R.pluck=function(e,t){return R.map(e,function(e){return e[t]})},R.where=function(e,t,n){return R.isEmpty(t)?n?null:[]:R[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},R.findWhere=function(e,t){return R.where(e,t,!0)},R.max=function(e,t,n){if(!t&&R.isArray(e)&&e[0]===+e[0]&&65535>e.length)return Math.max.apply(Math,e);if(!t&&R.isEmpty(e))return-1/0;var a={computed:-1/0,value:-1/0};return j(e,function(e,i,r){var s=t?t.call(n,e,i,r):e;s>=a.computed&&(a={value:e,computed:s})}),a.value},R.min=function(e,t,n){if(!t&&R.isArray(e)&&e[0]===+e[0]&&65535>e.length)return Math.min.apply(Math,e);if(!t&&R.isEmpty(e))return 1/0;var a={computed:1/0,value:1/0};return j(e,function(e,i,r){var s=t?t.call(n,e,i,r):e;a.computed>s&&(a={value:e,computed:s})}),a.value},R.shuffle=function(e){var t,n=0,a=[];return j(e,function(e){t=R.random(n++),a[n-1]=a[t],a[t]=e}),a};var T=function(e){return R.isFunction(e)?e:function(t){return t[e]}};R.sortBy=function(e,t,n){var a=T(t);return R.pluck(R.map(e,function(e,t,i){return{value:e,index:t,criteria:a.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,a=t.criteria;if(n!==a){if(n>a||void 0===n)return 1;if(a>n||void 0===a)return-1}return e.indexr;){var o=r+s>>>1;i>n.call(a,e[o])?r=o+1:s=o}return r},R.toArray=function(e){return e?R.isArray(e)?o.call(e):e.length===+e.length?R.map(e,R.identity):R.values(e):[]},R.size=function(e){return null==e?0:e.length===+e.length?e.length:R.keys(e).length},R.first=R.head=R.take=function(e,t,n){return null==e?void 0:null==t||n?e[0]:o.call(e,0,t)},R.initial=function(e,t,n){return o.call(e,0,e.length-(null==t||n?1:t))},R.last=function(e,t,n){return null==e?void 0:null==t||n?e[e.length-1]:o.call(e,Math.max(e.length-t,0))},R.rest=R.tail=R.drop=function(e,t,n){return o.call(e,null==t||n?1:t)},R.compact=function(e){return R.filter(e,R.identity)};var M=function(e,t,n){return j(e,function(e){R.isArray(e)?t?s.apply(n,e):M(e,t,n):n.push(e)}),n};R.flatten=function(e,t){return M(e,t,[])},R.without=function(e){return R.difference(e,o.call(arguments,1))},R.uniq=R.unique=function(e,t,n,a){R.isFunction(t)&&(a=n,n=t,t=!1);var i=n?R.map(e,n,a):e,r=[],s=[];return j(i,function(n,a){(t?a&&s[s.length-1]===n:R.contains(s,n))||(s.push(n),r.push(e[a]))}),r},R.union=function(){return R.uniq(c.apply(a,arguments))},R.intersection=function(e){var t=o.call(arguments,1);return R.filter(R.uniq(e),function(e){return R.every(t,function(t){return R.indexOf(t,e)>=0})})},R.difference=function(e){var t=c.apply(a,o.call(arguments,1));return R.filter(e,function(e){return!R.contains(t,e)})},R.zip=function(){for(var e=o.call(arguments),t=R.max(R.pluck(e,"length")),n=Array(t),a=0;t>a;a++)n[a]=R.pluck(e,""+a);return n},R.object=function(e,t){if(null==e)return{};for(var n={},a=0,i=e.length;i>a;a++)t?n[e[a]]=t[a]:n[e[a][0]]=e[a][1];return n},R.indexOf=function(e,t,n){if(null==e)return-1;var a=0,i=e.length;if(n){if("number"!=typeof n)return a=R.sortedIndex(e,t),e[a]===t?a:-1;a=0>n?Math.max(0,i+n):n}if(g&&e.indexOf===g)return e.indexOf(t,n);for(;i>a;a++)if(e[a]===t)return a;return-1},R.lastIndexOf=function(e,t,n){if(null==e)return-1;var a=null!=n;if(b&&e.lastIndexOf===b)return a?e.lastIndexOf(t,n):e.lastIndexOf(t);for(var i=a?n:e.length;i--;)if(e[i]===t)return i;return-1},R.range=function(e,t,n){1>=arguments.length&&(t=e||0,e=0),n=arguments[2]||1;for(var a=Math.max(Math.ceil((t-e)/n),0),i=0,r=Array(a);a>i;)r[i++]=e,e+=n;return r},R.bind=function(e,t){if(e.bind===x&&x)return x.apply(e,o.call(arguments,1));var n=o.call(arguments,2);return function(){return e.apply(t,n.concat(o.call(arguments)))}},R.partial=function(e){var t=o.call(arguments,1);return function(){return e.apply(this,t.concat(o.call(arguments)))}},R.bindAll=function(e){var t=o.call(arguments,1);return 0===t.length&&(t=R.functions(e)),j(t,function(t){e[t]=R.bind(e[t],e)}),e},R.memoize=function(e,t){var n={};return t||(t=R.identity),function(){var a=t.apply(this,arguments);return R.has(n,a)?n[a]:n[a]=e.apply(this,arguments)}},R.delay=function(e,t){var n=o.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},R.defer=function(e){return R.delay.apply(R,[e,1].concat(o.call(arguments,1)))},R.throttle=function(e,t){var n,a,i,r,s=0,o=function(){s=new Date,i=null,r=e.apply(n,a)};return function(){var c=new Date,l=t-(c-s);return n=this,a=arguments,0>=l?(clearTimeout(i),i=null,s=c,r=e.apply(n,a)):i||(i=setTimeout(o,l)),r}},R.debounce=function(e,t,n){var a,i;return function(){var r=this,s=arguments,o=function(){a=null,n||(i=e.apply(r,s))},c=n&&!a;return clearTimeout(a),a=setTimeout(o,t),c&&(i=e.apply(r,s)),i}},R.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}},R.wrap=function(e,t){return function(){var n=[e];return s.apply(n,arguments),t.apply(this,n)}},R.compose=function(){var e=arguments;return function(){for(var t=arguments,n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},R.after=function(e,t){return 0>=e?t():function(){return 1>--e?t.apply(this,arguments):void 0}},R.keys=w||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)R.has(e,n)&&(t[t.length]=n);return t},R.values=function(e){var t=[];for(var n in e)R.has(e,n)&&t.push(e[n]);return t},R.pairs=function(e){var t=[];for(var n in e)R.has(e,n)&&t.push([n,e[n]]);return t},R.invert=function(e){var t={};for(var n in e)R.has(e,n)&&(t[e[n]]=n);return t},R.functions=R.methods=function(e){var t=[];for(var n in e)R.isFunction(e[n])&&t.push(n);return t.sort()},R.extend=function(e){return j(o.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},R.pick=function(e){var t={},n=c.apply(a,o.call(arguments,1));return j(n,function(n){n in e&&(t[n]=e[n])}),t},R.omit=function(e){var t={},n=c.apply(a,o.call(arguments,1));for(var i in e)R.contains(n,i)||(t[i]=e[i]);return t},R.defaults=function(e){return j(o.call(arguments,1),function(t){if(t)for(var n in t)null==e[n]&&(e[n]=t[n])}),e},R.clone=function(e){return R.isObject(e)?R.isArray(e)?e.slice():R.extend({},e):e},R.tap=function(e,t){return t(e),e};var U=function(e,t,n,a){if(e===t)return 0!==e||1/e==1/t;if(null==e||null==t)return e===t;e instanceof R&&(e=e._wrapped),t instanceof R&&(t=t._wrapped);var i=l.call(e);if(i!=l.call(t))return!1;switch(i){case"[object String]":return e==t+"";case"[object Number]":return e!=+e?t!=+t:0==e?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if("object"!=typeof e||"object"!=typeof t)return!1;for(var r=n.length;r--;)if(n[r]==e)return a[r]==t;n.push(e),a.push(t);var s=0,o=!0;if("[object Array]"==i){if(s=e.length,o=s==t.length)for(;s--&&(o=U(e[s],t[s],n,a)););}else{var c=e.constructor,u=t.constructor;if(c!==u&&!(R.isFunction(c)&&c instanceof c&&R.isFunction(u)&&u instanceof u))return!1;for(var d in e)if(R.has(e,d)&&(s++,!(o=R.has(t,d)&&U(e[d],t[d],n,a))))break;if(o){for(d in t)if(R.has(t,d)&&!s--)break;o=!s}}return n.pop(),a.pop(),o};R.isEqual=function(e,t){return U(e,t,[],[])},R.isEmpty=function(e){if(null==e)return!0;if(R.isArray(e)||R.isString(e))return 0===e.length;for(var t in e)if(R.has(e,t))return!1;return!0},R.isElement=function(e){return!(!e||1!==e.nodeType)},R.isArray=y||function(e){return"[object Array]"==l.call(e)},R.isObject=function(e){return e===Object(e)},j(["Arguments","Function","String","Number","Date","RegExp"],function(e){R["is"+e]=function(t){return l.call(t)=="[object "+e+"]"}}),R.isArguments(arguments)||(R.isArguments=function(e){return!(!e||!R.has(e,"callee"))}),true&&(R.isFunction=function(e){return"function"==typeof e}),R.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},R.isNaN=function(e){return R.isNumber(e)&&e!=+e},R.isBoolean=function(e){return e===!0||e===!1||"[object Boolean]"==l.call(e)},R.isNull=function(e){return null===e},R.isUndefined=function(e){return void 0===e},R.has=function(e,t){return u.call(e,t)},R.noConflict=function(){return e._=t,this},R.identity=function(e){return e},R.times=function(e,t,n){for(var a=Array(e),i=0;e>i;i++)a[i]=t.call(n,i);return a},R.random=function(e,t){return null==t&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var C={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};C.unescape=R.invert(C.escape);var D={escape:RegExp("["+R.keys(C.escape).join("")+"]","g"),unescape:RegExp("("+R.keys(C.unescape).join("|")+")","g")};R.each(["escape","unescape"],function(e){R[e]=function(t){return null==t?"":(""+t).replace(D[e],function(t){return C[e][t]})}}),R.result=function(e,t){if(null==e)return null;var n=e[t];return R.isFunction(n)?n.call(e):n},R.mixin=function(e){j(R.functions(e),function(t){var n=R[t]=e[t];R.prototype[t]=function(){var e=[this._wrapped];return s.apply(e,arguments),$.call(this,n.apply(R,e))}})};var E=0;R.uniqueId=function(e){var t=++E+"";return e?e+t:t},R.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,L={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},N=/\\|'|\r|\n|\t|\u2028|\u2029/g;R.template=function(e,t,n){var a;n=R.defaults({},n,R.templateSettings);var i=RegExp([(n.escape||A).source,(n.interpolate||A).source,(n.evaluate||A).source].join("|")+"|$","g"),r=0,s="__p+='";e.replace(i,function(t,n,a,i,o){return s+=e.slice(r,o).replace(N,function(e){return"\\"+L[e]}),n&&(s+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),a&&(s+="'+\n((__t=("+a+"))==null?'':__t)+\n'"),i&&(s+="';\n"+i+"\n__p+='"),r=o+t.length,t}),s+="';\n",n.variable||(s="with(obj||{}){\n"+s+"}\n"),s="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+s+"return __p;\n";try{a=Function(n.variable||"obj","_",s)}catch(o){throw o.source=s,o}if(t)return a(t,R);var c=function(e){return a.call(this,e,R)};return c.source="function("+(n.variable||"obj")+"){\n"+s+"}",c},R.chain=function(e){return R(e).chain()};var $=function(e){return this._chain?R(e).chain():e};R.mixin(R),j(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=a[e];R.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!=e&&"splice"!=e||0!==n.length||delete n[0],$.call(this,n)}}),j(["concat","join","slice"],function(e){var t=a[e];R.prototype[e]=function(){return $.call(this,t.apply(this._wrapped,arguments))}}),R.extend(R.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),this.FirechatDefaultTemplates=this.FirechatDefaultTemplates||{},this.FirechatDefaultTemplates["templates/layout-full.html"]=function(obj){obj||(obj={});var __p="";with(_.escape,obj)__p+="
              \n
              \n
              \n\n
              \n
              \n
              \n\n
              \n
              \n
              ";return __p},this.FirechatDefaultTemplates["templates/layout-popout.html"]=function(obj){obj||(obj={});var __p="";with(_.escape,obj)__p+="
              \n
              \n\n
              \n
              \n
              ";return __p},this.FirechatDefaultTemplates["templates/message-context-menu.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
              \n\n
              ";return __p},this.FirechatDefaultTemplates["templates/message.html"]=function(obj){obj||(obj={});var __t,__p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
              \n
              \n",isSelfMessage||(__p+="\n\n"),__p+="
              \n
              \n"+(null==(__t=message)?"":__t)+"\n
              \n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt-alert.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
              \n
              "+__e(message)+"
              \n

              \n\n

              \n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt-create-room.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
              \n
              Give your chat room a name:
              \n\n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt-invitation.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
              \n
              "+__e(fromUserName)+"
              \n

              invited you to join

              \n
              "+__e(toRoomName)+"
              \n

              \n\n\n

              \n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt-invite-private.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
              \n
              Invite "+__e(userName)+" to "+__e(roomName)+"?
              \n

              \n\n\n

              \n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt-invite-reply.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
              \n
              "+__e(toUserName)+"
              \n

              \n",__p+="accepted"===status?" accepted your invite. ":" declined your invite. ",__p+="\n

              \n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt-user-mute.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
              \n
              "+__e(userName)+"
              \n

              \n\n\n

              \n
              ";return __p},this.FirechatDefaultTemplates["templates/prompt.html"]=function(obj){obj||(obj={});var __t,__p="",__e=_.escape;with(obj)__p+="";return __p},this.FirechatDefaultTemplates["templates/room-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
            • \n\n"+__e(name)+"\n\n
            • ";return __p},this.FirechatDefaultTemplates["templates/room-user-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
            • \n\n"+__e(name)+"",disableActions||(__p+="\n \n \n"),__p+="\n\n
            • ";return __p},this.FirechatDefaultTemplates["templates/room-user-search-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
            • \n\n",__p+=disableActions?"\n"+__e(name)+"\n":"\n"+__e(name)+"\n+\n",__p+="\n\n
            • ";return __p},this.FirechatDefaultTemplates["templates/tab-content.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
              \n
              \n
              \n
              \n\n\n
              \n
              ";return __p},this.FirechatDefaultTemplates["templates/tab-menu-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
            • \n"+__e(name)+"\n
            • ";return __p},this.FirechatDefaultTemplates["templates/user-search-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
            • \n\n",__p+=disableActions?"\n"+__e(name)+"\n":"\n"+__e(name)+"\n \n",__p+="\n\n
            • ";return __p},function(){Function.prototype.bind||(Function.prototype.bind=function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),n=this,a=function(){},i=function(){return n.apply(this instanceof a&&e?this:e,t.concat(Array.prototype.slice.call(arguments)))};return a.prototype=this.prototype,i.prototype=new a,i}),Object.keys=Object.keys||function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t}}(),function(e){function t(e,t){this._firebase=e,this._user=null,this._userId=null,this._userName=null,this._isModerator=!1,this._sessionId=null,this._events={},this._rooms={},this._presenceBits={},this._userRef=null,this._messageRef=this._firebase.child("room-messages"),this._roomRef=this._firebase.child("room-metadata"),this._privateRoomRef=this._firebase.child("room-private-metadata"),this._moderatorsRef=this._firebase.child("moderators"),this._suspensionsRef=this._firebase.child("suspensions"),this._usersOnlineRef=this._firebase.child("user-names-online"),this._options=t||{},this._options.numMaxMessages=this._options.numMaxMessages||50}var n=this,a=n.Firechat;t.noConflict=function(){return n.Firechat=a,t},n.Firechat=t,t.prototype={_loadUserMetadata:function(e){var t=this;this._userRef.transaction(function(e){return e&&e.id&&e.name?void 0:{id:t._userId,name:t._userName}},function(a,i,r){t._user=r.val(),t._moderatorsRef.child(t._userId).once("value",function(a){t._isModerator=!!a.val(),n.setTimeout(e,0)})})},_setupDataEvents:function(){this._firebase.root().child(".info/connected").on("value",function(e){if(e.val()===!0)for(var t=0;this._presenceBits>t;t++){var n=this._presenceBits[t],a=this._firebase.root().child(n.ref);a.onDisconnect().set(n.offlineValue),a.set(n.onlineValue)}},this);var e=this._userRef.child("sessions").push();this._sessionId=e.name(),this._queuePresenceOperation(e,!0,null);var t=this._usersOnlineRef.child(this._userName.toLowerCase()),n=t.child(this._sessionId);this._queuePresenceOperation(n,{id:this._userId,name:this._userName},null),this._userRef.on("value",this._onUpdateUser,this),this._userRef.child("invites").on("child_added",this._onFirechatInvite,this),this._userRef.child("notifications").on("child_added",this._onNotification,this)},_addEventCallback:function(e,t){this._events[e]=this._events[e]||[],this._events[e].push(t)},_getEventCallbacks:function(e){return this._events.hasOwnProperty(e)?this._events[e]:[]},_invokeEventCallbacks:function(e){var t=[],n=this._getEventCallbacks(e);Array.prototype.push.apply(t,arguments),t=t.slice(1);for(var a=0;n.length>a;a+=1)n[a].apply(null,t)},_queuePresenceOperation:function(e,t,n){e.onDisconnect().set(n),e.set(t),this._presenceBits[""+e]={ref:e,onlineValue:t,offlineValue:n}},_removePresenceOperation:function(t,n){var a=new e(t);a.onDisconnect().cancel(),a.set(n),delete this._presenceBits[t]},_onUpdateUser:function(e){this._user=e.val(),this._invokeEventCallbacks("user-update",this._user)},_onEnterRoom:function(e){this._invokeEventCallbacks("room-enter",e)},_onNewMessage:function(e,t){var n=t.val();n.id=t.name(),this._invokeEventCallbacks("message-add",e,n)},_onRemoveMessage:function(e,t){var n=t.name();this._invokeEventCallbacks("message-remove",e,n)},_onLeaveRoom:function(e){this._invokeEventCallbacks("room-exit",e)},_onNotification:function(t){var n=t.val();n.read||(("suspension"!==n.notificationType||n.data.suspendedUntil2&&(r=arguments[1]),a=r?a.limit(r):a,a.once("value",function(e){var t=e.val()||{},a={};for(var r in t)for(var s in t[r]){a[r]=t[r][s];break}n.setTimeout(function(){i(a)},0)})},t.prototype.getUsersByPrefix=function(e,t,a,i,r){var s=this._usersOnlineRef,o=e.toLowerCase();s=t?s.startAt(null,t):a?s.endAt(null,a):o?s.startAt(null,o):s.startAt(),s=i?s.limit(i):s,s.once("value",function(t){var a=t.val()||{},i={};for(var s in a){var c,l,u=a[s];for(var d in u){c=u[d].name,l=u[d].id;break}e.length>0&&0!==c.toLowerCase().indexOf(o)||(i[c]={name:c,id:l})}n.setTimeout(function(){r(i)},0)})},t.prototype.getRoom=function(e,t){this._roomRef.child(e).once("value",function(e){t(e.val())})},t.prototype.userIsModerator=function(){return this._isModerator},t.prototype.sessionIdGet=function(){return this._sessionId}}(Firebase),function(e){function t(t,n,a){if(!t)throw Error("FirechatUI: Missing required argument `firebaseRef`");if(!n)throw Error("FirechatUI: Missing required argument `el`");a=a||{},this._options=a,this._el=n,this._user=null,this._chat=new Firechat(t,a),this._roomQueue=[],this.maxLengthUsername=15,this.maxLengthUsernameDisplay=15,this.maxLengthRoomName=24,this.maxLengthMessage=120,this.maxUserSearchResults=100,this.urlPattern=/\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim,this.pseudoUrlPattern=/(^|[^\/])(www\.[\S]+(\b|$))/gim,this._renderLayout(),this.$wrapper=e("#firechat"),this.$roomList=e("#firechat-room-list"),this.$tabList=e("#firechat-tab-list"),this.$tabContent=e("#firechat-tab-content"),this.$messages={},this.$rateLimit={limitCount:10,limitInterval:1e4,limitWaitTime:3e4,history:{}},this._bindUIEvents(),this._bindDataEvents()}if(!e||170>parseInt(e().jquery.replace(/\./g,""),10))throw Error("jQuery 1.7 or later required!");var n=this,a=n.FirechatUI;if(n.FirechatUI=t,!self.FirechatDefaultTemplates)throw Error("Unable to find chat templates!");t.noConflict=function(){return n.FirechatUI=a,t},t.prototype={_bindUIEvents:function(){this._bindForHeightChange(),this._bindForTabControls(),this._bindForRoomList(),this._bindForUserRoomList(),this._bindForUserSearch(),this._bindForUserMuting(),this._bindForChatInvites(),this._bindForRoomListing(),this._setupTabs(),this._setupDropdowns(),this._bindTextInputFieldLimits()},_bindDataEvents:function(){this._chat.on("user-update",this._onUpdateUser.bind(this)),this._chat.on("room-enter",this._onEnterRoom.bind(this)),this._chat.on("room-exit",this._onLeaveRoom.bind(this)),this._chat.on("message-add",this._onNewMessage.bind(this)),this._chat.on("message-remove",this._onRemoveMessage.bind(this)),this._chat.on("room-invite",this._onChatInvite.bind(this)),this._chat.on("room-invite-response",this._onChatInviteResponse.bind(this)),this._chat.on("notification",this._onNotification.bind(this))},_renderLayout:function(){var t=FirechatDefaultTemplates["templates/layout-full.html"];e(this._el).html(t({maxLengthUsername:this.maxLengthUsername}))},_onUpdateUser:function(t){this._user=t;var n=this._user.muted||{};e('[data-event="firechat-user-mute-toggle"]').each(function(){var t=e(this).closest("[data-user-id]").data("user-id");e(this).toggleClass("red",!!n[t])});for(var a in n)e('.message[data-user-id="'+a+'"]').fadeOut()},_onEnterRoom:function(e){this.attachTab(e.id,e.name)},_onLeaveRoom:function(e){this.removeTab(e),this._roomQueue.length>0&&this._chat.enterRoom(this._roomQueue.shift(e))},_onNewMessage:function(e,t){var n=t.userId;this._user.muted&&this._user.muted[n]||this.showMessage(e,t)},_onRemoveMessage:function(e,t){this.removeMessage(e,t)},_onChatInvite:function(e){var t=this,n=FirechatDefaultTemplates["templates/prompt-invitation.html"],a=this.prompt("Invite",n(e));a.find("a.close").click(function(){a.remove(),t._chat.declineInvite(e.id)}),a.find("[data-toggle=accept]").click(function(){a.remove(),t._chat.acceptInvite(e.id)}),a.find("[data-toggle=decline]").click(function(){a.remove(),t._chat.declineInvite(e.id)})},_onChatInviteResponse:function(e){if(e.status){var t,n=this,a=FirechatDefaultTemplates["templates/prompt-invite-reply.html"];e.status&&"accepted"===e.status?(t=this.prompt("Accepted",a(e)),this._chat.getRoom(e.toRoomId,function(t){n.attachTab(e.toRoomId,t.name)})):t=this.prompt("Declined",a(e)),t.find("a.close").click(function(){t.remove()})}},_onNotification:function(e){if("warning"===e.notificationType)this.renderAlertPrompt("Warning","You are being warned for inappropriate messaging. Further violation may result in temporary or permanent ban of service.");else if("suspension"===e.notificationType){var t=e.data.suspendedUntil,n=Math.round((t-(new Date).getTime())/1e3),a="";if(n>0){if(n>7200){var i=Math.floor(n/3600);a=i+" hours, ",n-=3600*i}a+=Math.floor(n/60)+" minutes",this.renderAlertPrompt("Suspended","A moderator has suspended you for violating site rules. You cannot send messages for another "+a+".")}}}},t.prototype.initWithUser=function(e,t){var n=this;n._chat.initWithUser(e,t,function(e){n._user=e,n._chat.userIsModerator()&&n._bindSuperuserUIEvents(),n._chat.resumeSession()})},t.prototype._bindSuperuserUIEvents=function(){var t=this,n=function(){var t=e(this),n=t.closest("[data-message-id]").data("message-id"),a=e('[data-message-id="'+n+'"]').closest("[data-user-id]").data("user-id"),i=e('[data-message-id="'+n+'"]').closest("[data-room-id]").data("room-id");return{messageId:n,userId:a,roomId:i}},a=function(){e('[data-toggle="firechat-contextmenu"]').each(function(){e(this).remove()}),e("#firechat .message.highlighted").each(function(){e(this).removeClass("highlighted")})},i=function(i){var r,s=e(this),o=s.closest("[data-message-id]"),c=FirechatDefaultTemplates["templates/message-context-menu.html"],l=n.call(this,i);i.preventDefault(),a(),s.addClass("highlighted"),t._chat.getRoom(l.roomId,function(){r=e(c({id:o.data("message-id")})),r.css({left:i.clientX,top:i.clientY}).appendTo(t.$wrapper)})};e(document).bind("click",{self:this},function(e){e.button&&2==e.button||a()}),e(document).delegate('[data-class="firechat-message"]',"contextmenu",i),e(document).delegate('[data-event="firechat-user-warn"]',"click",function(e){var a=n.call(this,e);t._chat.warnUser(a.userId)}),e(document).delegate('[data-event="firechat-user-suspend-hour"]',"click",function(e){var a=n.call(this,e);t._chat.suspendUser(a.userId,3600)}),e(document).delegate('[data-event="firechat-user-suspend-day"]',"click",function(e){var a=n.call(this,e);t._chat.suspendUser(a.userId,86400)}),e(document).delegate('[data-event="firechat-message-delete"]',"click",function(e){var a=n.call(this,e);t._chat.deleteMessage(a.roomId,a.messageId)})},t.prototype._bindForHeightChange=function(){var t=e(this._el),n=null;setInterval(function(){var a=t.height();a!=n&&(n=a,e(".chat").each(function(){}))},500)},t.prototype._bindForTabControls=function(){var t=this;e(document).delegate('[data-event="firechat-close-tab"]',"click",function(){var n=e(this).closest("[data-room-id]").data("room-id");t._chat.leaveRoom(n)})},t.prototype._bindForRoomList=function(){var t=this;e("#firechat-btn-rooms").bind("click",function(){if(!e(this).parent().hasClass("open")){var n=(e(this),FirechatDefaultTemplates["templates/room-list-item.html"]),a=function(){var n=e(this).parent(),a=n.data("room-id"),i=n.data("room-name");t.$messages[a]?t.focusTab(a):t._chat.enterRoom(a,i)};t._chat.getRoomList(function(i){t.$roomList.empty();for(var r in i){var s=i[r];s.isRoomOpen=!!t.$messages[s.id];var o=e(n(s));o.children("a").bind("click",a),t.$roomList.append(o.toggle(!0))}})}})},t.prototype._bindForUserRoomList=function(){var t=this;e(document).delegate('[data-event="firechat-user-room-list-btn"]',"click",function(n){n.stopPropagation();var a=e(this),i=a.closest("[data-room-id]").data("room-id"),r=FirechatDefaultTemplates["templates/room-user-list-item.html"],s=a.data("target"),o=e("#"+s);o.empty(),t._chat.getUsersByRoom(i,function(n){for(var a in n)user=n[a],user.disableActions=user.id===t._user.id,user.nameTrimmed=t.trimWithEllipsis(user.name,t.maxLengthUsernameDisplay),user.isMuted=t._user.muted&&t._user.muted[user.id],o.append(e(r(user)));t.sortListLexicographically("#"+s)})})},t.prototype._bindForUserSearch=function(){var t=this,n=function(t){var n=e(this),i=n.data("target"),r=n.data("controls"),s=n.data("template"),o=n.val()||n.data("prefix")||"",c=n.data("startAt")||null,l=n.data("endAt")||null;t.preventDefault(),a(i,s,r,o,c,l)},a=function(n,a,i,r,s,o){var c=e("#"+n),l=e("#"+i),u=FirechatDefaultTemplates[a];t._chat.getUsersByPrefix(r,s,o,t.maxUserSearchResults,function(e){var n,a,i,s,o,d=0;c.empty();for(i in e){var h=e[i];if(h.disableActions=h.id===t._user.id,d+=1,c.append(u(h)),1===d)s=h.name.toLowerCase();else if(d>=t.maxUserSearchResults){o=h.name.toLowerCase();break}}l&&(n=l.find('[data-toggle="firechat-pagination-prev"]'),a=l.find('[data-toggle="firechat-pagination-next"]'),o?a.data("event","firechat-user-search").data("startAt",o).data("prefix",r).removeClass("disabled").removeAttr("disabled"):a.data("event",null).data("startAt",null).data("prefix",null).addClass("disabled").attr("disabled","disabled"))})};e(document).delegate('[data-event="firechat-user-search"]',"keyup",n),e(document).delegate('[data-event="firechat-user-search"]',"click",n),e(document).delegate('[data-event="firechat-user-search-btn"]',"click",function(t){t.stopPropagation();var n=e(this).next("div.dropdown-menu").find("input");n.focus(),n.trigger(jQuery.Event("keyup"))}),e(document).delegate('[data-event="firechat-user-search"]',"click",function(e){e.stopPropagation()})},t.prototype._bindForUserMuting=function(){var t=this;e(document).delegate('[data-event="firechat-user-mute-toggle"]',"click",function(n){var a=e(this),i=a.closest("[data-user-id]").data("user-id"),r=a.closest("[data-user-name]").data("user-name"),s=a.hasClass("red"),o=FirechatDefaultTemplates["templates/prompt-user-mute.html"];if(n.preventDefault(),s)t._chat.toggleUserMute(i);else{var c=t.prompt("Mute User?",o({userName:r}));c.find("a.close").first().click(function(){c.remove()}),c.find("[data-toggle=decline]").first().click(function(){c.remove()}),c.find("[data-toggle=accept]").first().click(function(){t._chat.toggleUserMute(i),c.remove()})}})},t.prototype._bindForChatInvites=function(){var t=this,n=function(){var n,a=e(this),i=a.closest("[data-user-id]").data("user-id"),r=a.closest("[data-room-id]").data("room-id"),s=a.closest("[data-user-name]").data("user-name"),o=FirechatDefaultTemplates["templates/prompt-invite-private.html"];t._chat.getRoom(r,function(e){n=t.prompt("Invite",o({userName:s,roomName:e.name})),n.find("a.close").click(function(){n.remove()}),n.find("[data-toggle=decline]").click(function(){n.remove()}),n.find("[data-toggle=accept]").first().click(function(){n.remove(),t._chat.inviteUser(i,r,e.name)})})},a=function(){var n,a=e(this),i=a.closest("[data-user-id]").data("user-id"),r=a.closest("[data-user-name]").data("user-name"),s=FirechatDefaultTemplates["templates/prompt-invite-private.html"];i&&r&&(n=t.prompt("Private Invite",s({userName:r,roomName:"Private Chat"})),n.find("a.close").click(function(){n.remove()}),n.find("[data-toggle=decline]").click(function(){n.remove()}),n.find("[data-toggle=accept]").first().click(function(){n.remove();var e="Private Chat";t._chat.createRoom(e,"private",function(n){t._chat.inviteUser(i,n,e)})}))};e(document).delegate('[data-event="firechat-user-chat"]',"click",a),e(document).delegate('[data-event="firechat-user-invite"]',"click",n)},t.prototype._bindForRoomListing=function(){var t=this,n=e("#firechat-btn-create-room-prompt"),a=e("#firechat-btn-create-room");n.bind("click",function(){t.promptCreateRoom()}),a.bind("click",function(){var n=e("#firechat-input-room-name").val();e("#firechat-prompt-create-room").remove(),t._chat.createRoom(n)})},t.prototype._setupTabs=function(){var t=function(t){var a,i,r=t,s=r.closest("ul:not(.dropdown-menu)"),o=r.attr("data-target"),c=s.find(".active:last a")[0];o||(o=r.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),r.parent("li").hasClass("active")||(i=e.Event("show",{relatedTarget:c}),r.trigger(i),i.isDefaultPrevented()||(a=e(o),n(r.parent("li"),s),n(a,a.parent(),function(){r.trigger({type:"shown",relatedTarget:c})})))},n=function(t,n,a){function i(){r.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?t.addClass("in"):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),a&&a()}var r=n.find("> .active"),s=a&&e.support.transition&&r.hasClass("fade");s?r.one(e.support.transition.end,i):i(),r.removeClass("in")};e(document).delegate('[data-toggle="tab"]',"click",function(n){n.preventDefault(),t(e(this))})},t.prototype._setupDropdowns=function(){var t=function(){var t=e(this),i=a(t),r=i.hasClass("open");if(!t.is(".disabled, :disabled"))return n(),r||i.toggleClass("open"),t.focus(),!1},n=function(){e("[data-toggle=dropdown]").each(function(){a(e(this)).removeClass("open")})},a=function(t){var n,a=t.attr("data-target");return a||(a=t.attr("href"),a=a&&/#/.test(a)&&a.replace(/.*(?=#[^\s]*$)/,"")),n=a&&e(a),n&&n.length||(n=t.parent()),n};e(document).bind("click",n).delegate(".dropdown-menu","click",function(e){e.stopPropagation()}).delegate("[data-toggle=dropdown]","click",t)},t.prototype._bindTextInputFieldLimits=function(){e("body").delegate('input[data-provide="limit"], textarea[data-provide="limit"]',"keyup",function(){var t=e(this),n=e(t.data("counter")),a=t.attr("maxlength"),i=t.val().length;n.html(Math.max(0,a-i))})},t.prototype.renderAlertPrompt=function(e,t){var n=FirechatDefaultTemplates["templates/prompt-alert.html"],a=this.prompt(e,n({message:t}));a.find(".close").click(function(){a.remove()})},t.prototype.toggleInputs=function(t){e("#firechat-tab-content textarea").each(function(){var n=e(this);t?e(this).val(""):e(this).val("You have exceeded the message limit, please wait before sending."),n.prop("disabled",!t)}),e("#firechat-input-name").prop("disabled",!t)},t.prototype.attachTab=function(t,n){var a=this;if(this.$messages[t])return this.focusTab(t),void 0;var i={id:t,name:n},r=FirechatDefaultTemplates["templates/tab-content.html"],s=e(r(i));this.$tabContent.prepend(s);var o=e("#firechat-messages"+t);this.$messages[t]=o;var c=s.find("textarea").first();c.bind("keydown",function(e){var n=a.trimWithEllipsis(c.val(),a.maxLengthMessage);return 13===e.which&&""!==n?(c.val(""),a._chat.sendMessage(t,n),!1):void 0});var l=FirechatDefaultTemplates["templates/tab-menu-item.html"],u=e(l(i));this.$tabList.prepend(u),u.bind("shown",function(){o.scrollTop(o[0].scrollHeight)});var d=this.$tabList.children("li"),h=Math.floor(e("#firechat-tab-list").width()/d.length);this.$tabList.children("li").css("width",h),this.$roomList.children("[data-room-id="+t+"]").children("a").addClass("highlight"),e("#firechat-btn-room-user-list-"+t).bind("click",function(){a.sortListLexicographically("#firechat-room-user-list-"+t)}),this.focusTab(t)},t.prototype.focusTab=function(e){if(this.$messages[e]){var t=this.$tabList.find("[data-room-id="+e+"]").find("a");t.length&&t.first().trigger("click")}},t.prototype.removeTab=function(t){delete this.$messages[t],this.$tabContent.find("[data-room-id="+t+"]").remove(),this.$tabList.find("[data-room-id="+t+"]").remove();var n=this.$tabList.children("li"),a=Math.floor(e("#firechat-tab-list").width()/n.length);this.$tabList.children("li").css("width",a),this.$tabList.find("[data-toggle=tab]").first().trigger("click"),this.$roomList.children("[data-room-id="+t+"]").children("a").removeClass("highlight")},t.prototype.showMessage=function(t,n){var a=this,i={id:n.id,localtime:this.formatTime(n.timestamp),message:n.message||"",userId:n.userId,name:n.name,type:n.type||"default",isSelfMessage:n.userId==this._user.id};i.message=_.map(i.message.split(" "),function(e){return a.urlPattern.test(e)||a.pseudoUrlPattern.test(e)?a.linkify(encodeURI(e)):_.escape(e)}).join(" "),i.message=this.trimWithEllipsis(i.message,this.maxLengthMessage);var r=FirechatDefaultTemplates["templates/message.html"],s=e(r(i)),o=this.$messages[t];if(o){var c=!1;o.scrollTop()/(o[0].scrollHeight-o[0].offsetHeight)>=.95?c=!0:o[0].scrollHeight<=o.height()&&(c=!0),o.append(s),c&&o.scrollTop(o[0].scrollHeight)}},t.prototype.removeMessage=function(t,n){e('.message[data-message-id="'+n+'"]').remove()},t.prototype.sortListLexicographically=function(t){e(t).children("li").sort(function(t,n){var a=e(t).text().toUpperCase(),i=e(n).text().toUpperCase();return i>a?-1:a>i?1:0}).appendTo(t)},t.prototype.trimWithEllipsis=function(e,t){return e=e.replace(/^\s\s*/,"").replace(/\s\s*$/,""),t&&t>=e.length?e:e.substring(0,t)+"..."},t.prototype.formatTime=function(e){var t=e?new Date(e):new Date,n=t.getHours()||12,a=""+t.getMinutes(),i=t.getHours()>=12?"pm":"am";return n=n>12?n-12:n,a=2>a.length?"0"+a:a,""+n+":"+a+i},t.prototype.promptCreateRoom=function(){var e=this,t=FirechatDefaultTemplates["templates/prompt-create-room.html"],n=this.prompt("Create Public Room",t({maxLengthRoomName:this.maxLengthRoomName,isModerator:e._chat.userIsModerator()}));n.find("a.close").first().click(function(){n.remove()}),n.find("[data-toggle=submit]").first().click(function(){var t=n.find("[data-input=firechat-room-name]").first().val();""!==t&&(e._chat.createRoom(t,"public"),n.remove())}),n.find("[data-input=firechat-room-name]").first().focus(),n.find("[data-input=firechat-room-name]").first().bind("keydown",function(t){if(13===t.which){var a=n.find("[data-input=firechat-room-name]").first().val();if(""!==a)return e._chat.createRoom(a,"public"),n.remove(),!1}})},t.prototype.prompt=function(t,n){var a,i=FirechatDefaultTemplates["templates/prompt.html"];return a=e(i({title:t,content:n})).css({top:this.$wrapper.position().top+.333*this.$wrapper.height(),left:this.$wrapper.position().left+.125*this.$wrapper.width(),width:.75*this.$wrapper.width()}),this.$wrapper.append(a.removeClass("hidden")),a},t.prototype.linkify=function(e){var t=this;return e.replace(t.urlPattern,'$&').replace(t.pseudoUrlPattern,'$1$2')}}(jQuery); \ No newline at end of file diff --git a/firechat/0.1.2/firechat-default.css b/firechat/0.1.2/firechat-default.css new file mode 100644 index 0000000..91a6c1a --- /dev/null +++ b/firechat/0.1.2/firechat-default.css @@ -0,0 +1,944 @@ +@charset "UTF-8"; +/* Boilerplate: Reset +============================================================ */ +#firechat div, +#firechat span, +#firechat applet, +#firechat object, +#firechat iframe, +#firechat h1, +#firechat h2, +#firechat h3, +#firechat h4, +#firechat h5, +#firechat h6, +#firechat p, +#firechat blockquote, +#firechat pre, +#firechat a, +#firechat abbr, +#firechat acronym, +#firechat address, +#firechat big, +#firechat cite, +#firechat code, +#firechat del, +#firechat dfn, +#firechat em, +#firechat img, +#firechat ins, +#firechat kbd, +#firechat q, +#firechat s, +#firechat samp, +#firechat small, +#firechat strike, +#firechat strong, +#firechat sub, +#firechat sup, +#firechat tt, +#firechat var, +#firechat b, +#firechat u, +#firechat i, +#firechat center, +#firechat dl, +#firechat dt, +#firechat dd, +#firechat ol, +#firechat ul, +#firechat li, +#firechat fieldset, +#firechat form, +#firechat label, +#firechat legend, +#firechat table, +#firechat caption, +#firechat tbody, +#firechat tfoot, +#firechat thead, +#firechat tr, +#firechat th, +#firechat td, +#firechat article, +#firechat aside, +#firechat canvas, +#firechat details, +#firechat embed, +#firechat figure, +#firechat figcaption, +#firechat footer, +#firechat header, +#firechat hgroup, +#firechat menu, +#firechat nav, +#firechat output, +#firechat ruby, +#firechat section, +#firechat summary, +#firechat time, +#firechat mark, +#firechat audio, +#firechat video { + border: 0; + font-size: 12px; + font-family: arial, helvetica, sans-serif; + vertical-align: baseline; + margin: 0; + padding: 0; +} +#firechat article, +#firechat aside, +#firechat details, +#firechat figcaption, +#firechat figure, +#firechat footer, +#firechat header, +#firechat hgroup, +#firechat menu, +#firechat nav, +#firechat section { + display: block; +} +#firechat body { + line-height: 1; +} +#firechat ol, +#firechat ul { + list-style: none; +} +#firechat blockquote, +#firechat q { + quotes: none; +} +#firechat blockquote:before, +#firechat blockquote:after, +#firechat q:before, +#firechat q:after { + content: none; +} +#firechat table { + border-collapse: collapse; + border-spacing: 0; +} +/* Boilerplate: Mixins +============================================================ */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +/* Boilerplate: Responsive Layout +============================================================ */ +#firechat { + color: #333; + text-align: left; +} +#firechat .center { + float: none !important; + margin-left: auto !important; + margin-right: auto !important; +} +#firechat .left { + float: left !important; +} +#firechat .right { + float: right !important; +} +#firechat .alignleft { + text-align: left !important; +} +#firechat .alignright { + text-align: right !important; +} +#firechat .aligncenter { + text-align: center !important; +} +#firechat .hidden { + display: none !important; +} +#firechat .row { + clear: both; +} +#firechat .fifth, +#firechat .fivesixth, +#firechat .fourfifth, +#firechat .half, +#firechat .ninetenth, +#firechat .quarter, +#firechat .sevententh, +#firechat .sixth, +#firechat .tenth, +#firechat .third, +#firechat .threefifth, +#firechat .threequarter, +#firechat .threetenth, +#firechat .twofifth, +#firechat .twothird, +#firechat .full { + margin-left: 2.127659574468085%; + float: left; + min-height: 1px; +} +#firechat .fifth:first-child, +#firechat .fivesixth:first-child, +#firechat .fourfifth:first-child, +#firechat .half:first-child, +#firechat .ninetenth:first-child, +#firechat .quarter:first-child, +#firechat .sevententh:first-child, +#firechat .sixth:first-child, +#firechat .tenth:first-child, +#firechat .third:first-child, +#firechat .threefifth:first-child, +#firechat .threequarter:first-child, +#firechat .threetenth:first-child, +#firechat .twofifth:first-child, +#firechat .twothird:first-child, +#firechat .full:first-child { + margin-left: 0; +} +#firechat .tenth { + width: 8.08510638297872%; +} +#firechat .sixth { + width: 14.893617021276595%; +} +#firechat .fifth { + width: 18.297872340425535%; +} +#firechat .quarter { + width: 23.404255319148938%; +} +#firechat .threetenth { + width: 26.3829787235%; +} +#firechat .third { + width: 31.914893617021278%; +} +#firechat .twofifth { + width: 38.72340425531915%; +} +#firechat .half { + width: 48.93617021276596%; +} +#firechat .sevententh { + width: 58.7234042555%; +} +#firechat .threefifth { + width: 59.14893617021278%; +} +#firechat .twothird { + width: 65.95744680851064%; +} +#firechat .threequarter { + width: 74.46808510638297%; +} +#firechat .ninetenth { + width: 74.8936170215%; +} +#firechat .fourfifth { + width: 79.57446808510639%; +} +#firechat .fivesixth { + width: 82.9787234042553%; +} +#firechat .full { + width: 100%; +} +#firechat .clipped { + overflow: hidden; +} +#firechat strong { + font-weight: bold; +} +#firechat em { + font-style: italic; +} +#firechat label { + display: block; +} +#firechat a { + color: #005580; +} +#firechat a:visited, +#firechat a:hover, +#firechat a:active { + color: #005580; +} +#firechat p { + margin: 10px 0; +} +#firechat h1, +#firechat h2, +#firechat h3, +#firechat h4, +#firechat h5, +#firechat h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; +} +#firechat h1, +#firechat h2, +#firechat h3 { + line-height: 40px; +} +#firechat h1 { + font-size: 38.5px; +} +#firechat h2 { + font-size: 31.5px; +} +#firechat h3 { + font-size: 24.5px; +} +#firechat h4 { + font-size: 17.5px; +} +#firechat h5 { + font-size: 14px; +} +#firechat h6 { + font-size: 11.9px; +} +#firechat small { + font-size: 90%; +} +/* Component: Tabs +============================================================ */ +#firechat .nav { + list-style: none; +} +#firechat .nav > li > a { + display: block; + background-color: #eeeeee; + text-decoration: none; + overflow: hidden; + white-space: nowrap; +} +#firechat .nav > li > a:hover, +#firechat .nav > li > a:focus { + background-color: #ffffff; +} +#firechat .nav-tabs { + border-bottom: 1px solid #ddd; + clear: both; +} +#firechat .nav-tabs > li { + float: left; + margin-bottom: -1px; + max-width: 45%; +} +#firechat .nav-tabs > li > a { + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topleft: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 4px; + padding: 4px 8px; + margin-right: 2px; + line-height: 20px; + border: 1px solid transparent; + border-color: #cccccc; +} +#firechat .nav-tabs > .active > a, +#firechat .nav-tabs > .active > a:hover, +#firechat .nav-tabs > .active > a:focus { + border-bottom-color: transparent; + background-color: #ffffff; + cursor: default; +} +#firechat .tab-content { + overflow: auto; +} +#firechat .tab-content > .tab-pane { + display: none; +} +#firechat .tab-content > .active { + display: block; + background-color: #ffffff; +} +/* Component: Dropdowns +============================================================ */ +#firechat .caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; + margin-top: 8px; + margin-left: 2px; +} +#firechat .dropdown { + position: relative; +} +#firechat .dropdown-toggle { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; +} +#firechat .dropdown-toggle:focus, +#firechat .dropdown-toggle:active { + outline: none; + text-decoration: none; +} +#firechat .dropdown-toggle.btn { + padding: 4px 0 0; + height: 22px; +} +#firechat .dropdown-menu { + *zoom: 1; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; + z-index: 1000; + display: none; + float: left; + position: absolute; + top: 100%; + left: 0; + width: 100%; + background-color: #ffffff; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + min-width: 98%; + padding: 0; + margin: -1px 0 0; +} +#firechat .dropdown-menu:before, +#firechat .dropdown-menu:after { + display: table; + content: ""; + line-height: 0; +} +#firechat .dropdown-menu:after { + clear: both; +} +#firechat .dropdown-menu ul { + background-color: #ffffff; + list-style: none; + overflow-y: scroll; + max-height: 300px; +} +#firechat .dropdown-menu ul > li > a { + display: block; + padding: 1px 1px 1px 3px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} +#firechat .dropdown-menu ul > li > a.highlight { + background-color: #d9edf7; +} +#firechat .dropdown-menu ul > li > a:hover, +#firechat .dropdown-menu ul > li > a:focus, +#firechat .dropdown-menu ul > .active > a, +#firechat .dropdown-menu ul > .active > a:hover, +#firechat .dropdown-menu ul > .active > a:focus { + text-decoration: none; + color: #000000; + background-color: #d9edf7; + outline: 0; +} +#firechat .dropdown-menu ul > .disabled > a, +#firechat .dropdown-menu ul > .disabled > a:hover, +#firechat .dropdown-menu ul > .disabled > a:focus { + color: #999999; + text-decoration: none; + background-color: transparent; + background-image: none; + cursor: default; +} +#firechat .dropdown-header { + position: relative; + width: 100%; + padding: 10px 0; + background-color: #eeeeee; + border-bottom: 1px solid #cccccc; +} +#firechat .dropdown-footer { + position: relative; + width: 100%; + padding: 10px 0px; + background-color: #eeeeee; + border-top: 1px solid #cccccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +#firechat .open { + *z-index: 1000; +} +#firechat .open > .dropdown-menu { + display: block; + border: 1px solid #cccccc; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; +} +#firechat .open > .dropdown-toggle { + outline: none; + text-decoration: none; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topleft: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 4px; +} +/* Component: Prompts +============================================================ */ +#firechat .prompt-wrapper { + position: absolute; + z-index: 1000; +} +#firechat .prompt { + position: absolute; + z-index: 1001; + background-color: #ffffff; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.45); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.45); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.45); +} +#firechat .prompt-header { + padding: 4px 8px; + font-weight: bold; + background-color: #eeeeee; + border: 1px solid #cccccc; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topleft: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: 4px; +} +#firechat .prompt-header a.close { + opacity: 0.6; + font-size: 13px; + margin-top: 2px; +} +#firechat .prompt-header a.close:hover { + opacity: 0.9; +} +#firechat .prompt-body { + background-color: #ffffff; + padding: 4px 8px; + border-left: 1px solid #cccccc; + border-right: 1px solid #cccccc; +} +#firechat .prompt-footer { + padding: 4px 8px; + background-color: #eeeeee; + border: 1px solid #cccccc; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; +} +#firechat .prompt-background { + background-color: #333333; + border: 1px solid #333333; + opacity: 0.8; + z-index: 1000; + height: 100%; + width: 100%; +} +/* Component: Buttons +============================================================ */ +#firechat .btn { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + height: 24px; + display: inline-block; + *display: inline; + *zoom: 1; + padding: 2px 5px; + margin-bottom: 0; + text-align: center; + vertical-align: middle; + cursor: pointer; + color: #333333; + font-size: 12px; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e6e6e6; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + border: 1px solid #cccccc; + *border: 0; + border-bottom-color: #b3b3b3; + *margin-left: .3em; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} +#firechat .btn:hover, +#firechat .btn:focus, +#firechat .btn:active, +#firechat .btn.active, +#firechat .btn.disabled, +#firechat .btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; + outline: 0; +} +#firechat .btn:active, +#firechat .btn.active { + background-color: #cccccc; +} +#firechat .btn:first-child { + *margin-left: 0; +} +#firechat .btn:hover, +#firechat .btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +#firechat .btn.active, +#firechat .btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} +#firechat .btn.disabled, +#firechat .btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); +} +#firechat .btn.disabled:active, +#firechat .btn[disabled]:active { + -webkit-box-shadow: inherit; + -moz-box-shadow: inherit; + box-shadow: inherit; + background-color: #e6e6e6; +} +/* Component: Context Menu +============================================================ */ +#firechat .contextmenu { + position: fixed; + z-index: 1001; + min-width: 150px; + border: 1px solid #cccccc; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +#firechat .contextmenu ul { + background-color: #ffffff; + list-style: none; +} +#firechat .contextmenu ul > li > a { + display: block; + padding: 3px 10px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} +#firechat .contextmenu ul > li > a.highlight { + background-color: #d9edf7; +} +#firechat .contextmenu ul > li > a:hover, +#firechat .contextmenu ul > li > a:focus { + text-decoration: none; + color: #ffffff; + background-color: #0081c2; + outline: 0; +} +/* Custom Styles +============================================================ */ +#firechat { + padding: 0; + font-family: sans-serif; + font-size: 12px; + line-height: 18px; +} +#firechat input, +#firechat textarea { + width: 100%; + font-family: sans-serif; + font-size: 12px; + line-height: 18px; + padding: 2px 5px; + border: 1px solid #cccccc; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +#firechat input:-moz-placeholder, +#firechat textarea:-moz-placeholder { + color: #aaaaaa; +} +#firechat input:-ms-input-placeholder, +#firechat textarea:-ms-input-placeholder { + color: #aaaaaa; +} +#firechat input::-webkit-input-placeholder, +#firechat textarea::-webkit-input-placeholder { + color: #aaaaaa; +} +#firechat input[disabled], +#firechat textarea[disabled] { + background-color: #eeeeee; +} +#firechat input { + height: 24px; +} +#firechat textarea { + resize: none; + height: 40px; +} +#firechat .search-wrapper { + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; + border: 1px solid #cccccc; + margin: 0 5px; + padding: 2px 5px; + background: #ffffff; +} +#firechat .search-wrapper > input[type=text] { + padding-left: 0px; + border: none; +} +#firechat .search-wrapper > input[type=text]:focus, +#firechat .search-wrapper > input[type=text]:active { + outline: 0; +} +#firechat .chat { + overflow: auto; + -ms-overflow-x: hidden; + overflow-x: hidden; + height: 290px; + position: relative; + margin-bottom: 5px; + border: 1px solid #cccccc; + border-top: none; + overflow-y: scroll; +} +#firechat .chat textarea { + overflow: auto; + vertical-align: top; +} +#firechat .message { + color: #333; + padding: 3px 5px; + border-bottom: 1px solid #ccc; +} +#firechat .message.highlighted { + background-color: #d9edf7; +} +#firechat .message .name { + font-weight: bold; + overflow-x: hidden; +} +#firechat .message.message-self { + color: #2675ab; +} +#firechat .message:nth-child(odd) { + background-color: #f9f9f9; +} +#firechat .message:nth-child(odd).highlighted { + background-color: #d9edf7; +} +#firechat .message:nth-child(odd).message-local { + background-color: #effafc; +} +#firechat .message-content { + word-wrap: break-word; + padding-right: 45px; +} +#firechat .message-content.red { + color: red; +} +#firechat .message.message-notification .message-content { + font-style: italic; +} +#firechat ul::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; +} +#firechat ul::-webkit-scrollbar-thumb { + border-radius: 4px; + -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); +} +#firechat #firechat-header { + padding: 6px 0 0 0; + height: 40px; +} +#firechat #firechat-tabs { + height: 435px; +} +#firechat #firechat-tab-list { + background-color: #ffffff; +} +#firechat #firechat-tab-content { + width: 100%; + background-color: #ffffff; +} +#firechat .tab-pane-menu { + border: 1px solid #ccc; + border-top: none; + vertical-align: middle; + padding-bottom: 5px; +} +#firechat .tab-pane-menu .dropdown { + margin: 5px 0 0 5px; +} +#firechat .tab-pane-menu > .icon { + margin: 5px 2px 0; +} +#firechat .icon { + display: inline-block; + *margin-right: .3em; + line-height: 20px; + vertical-align: middle; + background-repeat: no-repeat; + padding: 0; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL8AAAANEAYAAACoeGM7AAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAM80lEQVR42t2cZ4xWRRfHZ3dhwbK7CJZYYiOxV+wlUQzCKmBBsWPvYjQiaiyxYUwULLErltgrNpRF4q5iV0BRLFE0diUi7LIq6Pqw74f//rzO4Zm9d+5z9c37ni//3OfemXtm5sypc58q55xzrrpauHSpcOONheedJ9x9d2Hv3sLvvxfefbfw5puFf/5Zvr+8VFUl7Ozs/rkNNxTef79w222F77wjPOoo4ccfF/O+UDvGzTVEf8wH1zU1wlJJePTRwv33Fx57rPDnn/1+wbzzG+IXvrhmPUPzY/kJEc/HzqslK1fLLy8cMUK4667CFVYQ/vqr8PXXhU884f8eL6eXXKJ2wqSd3tDY2KOHc84NGqSRnnCC7tbVCdvbhRMnasWnTdt5Z+eca2pK6797Yh6ef1641lrCPfYQfv21UNwl62qv115b2Nws/PZb4d57C3/7LftalaOQ/C63nPCII4Tsg6228scHffqp8LHHhPfe6/8eu49feEH44YfCq64S/v67cMGC7tuvu65w6FDhllsKkS/0zoQJaZzMmSO+JaEzZmgkY8b88Ydzzs2b19HhnHNffSWJ6tVLM1cq/fKLrjXiUaPUrn9/XZ9yip6rqVmyxDnnnnrKvPbAA4V6TTJxP/zgT4zdyC+9JOzXz++PgceSVSS1tUJtE+c22si/f+21Pj9Wwd54o//8ZpsJd9xR2KtX9+8vmkLzcsABPt9z5wr32af88xiOf4oaGoSHH17M+/LKg203aJDwiy+EVh5DyHzuuWcxfDn31lvOOffMM7Nn6z1SsZ2dn30m/OQT4eefC7/8UvjBB0LaVzav7D873vfeE6LQISvv3Of50P6OJetgWFp5ZeFzz5V/b1bEEd1333x80g+O4mWXCXEQMDB33CG8/XYhBhuDmMZnOn30kZ5/+23nnGtpefNN55wbP96Xl/nzhYsWCdvauEbeQP1Of6US/XRZfDz8Bx8U9uwpPPFE4eOPC/EMVl9dOG6ccORI4Q03CA87LHbA5RcCwgO47TbhokXCYcOERCKQ7GJiMLiPokeQ2QCjRwtvukkY6zHg0V10kXDAAH++EIz77hM++aRwxRWFq64q3H57v9/+/YVPPy2cOFF47rnCNE8kll/Gi4eIAWU9v/vOnz8MA4qjb18hG51+2JiLF+ebX/hA4U+b5t+/7jrhAw8IkQ88WvnPiZzi4Q0eXL6/dEJhawaGDydmk4uEQmhu1kj79tVImb8RI8TZwIG0p78ddnAubOhDhIcPIXd4nnh4OHYYzPXXF7K/ed5GBLb/NLKRFOtMfyCZAiIKns8aSVp9xP4i8nv//Ti+icjgl0iDeUsjpKDSyJYeqqs1A+3taDBpkn79Qi5YW9vf5wXq0YP+1L5U6loAUjpW4WPhQhN00EFC2SXnDj1UiAF44w2hTWnEEqkcCIWJYkShQNbDIIJZZx2h9Xhs/7EpFDwCNs6UKf584tGQsiFCGTJEuMEG/jxBzBe/H3+8cJddhBiAZ58thl/eM2uW8MgjhWyAyy8XojhRUGuuKayvF7Jhf/pJ+O67wltvFU6dGscv78fTgg45RPjII+XbYZA23bT8/Vtu8ecBDy9MpHS0EROFr1B76lQp7r32su20EwYMkGSOG4dq4I0a4fDh9G9TQN0TKR0iB8aD3G+9tRDHDoPPenKf53GYiABiPWm7f046SUgKDs8aQwxlVfiQTVXh0BChxir+kDol5WPv24im+Ahcb6iu9jVnR4euGX9oXsLj7GKYHP68eUI8ANsRA7WKE8sNkUopdvgJsW0QUBbc3oewl6GQs9LUDgr85JOF5OZHjRKyESZNEp55ppBIC7IbxgoSkQzt2OgXX1wMv9RCFBAuG4HgSREKU0vB42Jjo6gxtPvtJ8TzTM91+kQOf731hHj4KHybA0bRK8B1bpNNhAqkE8OH40CKLZ3I4a+0kq6l8JuapPAbGx991Lm/r5s8+aFDlWOdOVMuS0ODNm5bm1RWSwsSrJY2FZVGRFYYYgwt+wO5EpeJ4eaa+zxPexQ+/Wcl+iFCxeBvs43QRujW048lu09Y16KI/m3Ewu8h/VS55w/5b7AGMrbW5lyXImQh8EhtqGBDGOu525QDnm5RhOcBzZ8vfO01IYIaev/Mmf5zpALsfSiv5bYLfeqpQhSUrYHY92GYbD+shx0XKSOKlrFk1xHFTQRon7M1FNtepaOErwsvFBIqkzKKlQ8MDoTnCpFKwyCi2FZbTYhDgydO6gfaaaesnFC0xQcUUQwlBi6VpPCHDJGKmDyZGWMldY2nPmmSP5NEdlkJRYSCZr7FRbiIzTX3eZ72tiiclZjf444rzyd6hkid9+fNCNhxvfJKvn5C/YVy9nZVYw89ZCdfI6TVEuCLCI4Zhp+Ojq6FIAdLFR0PjpQOFpyFsQMidw5ZxRo7ATaEI6fMgFD8FJsxXCgee00qSiWPJNfKaYLp08u/P1YQ4ZfxWgWDAcOjZmMR8G+3nZBTPbwffhYuFJ5zjhCPKi9ZBUzkh8dpBTkk2KSKLr1UiAIJEYYhK2GQIOto4OGj8NdYQ4hck8tnvkmtQSGDHKK6On8nUNtIPHwUvp05SeSwYTI1s2YpJhk5kliO/uP4Qe6IxMnhk9JhXqyCtQ4Oz9Oe/nwzl05EtJbI5fMeTs8QscIP+zwtEuA+csz+fvjhOH5tf6SSoZCjEipeKw5MVj12PemZ9tXVvunt2dOuHE+K06oqvbG2lp2ikS1ZIknv3burO45lXn+9kKItOfzQwhO6jR0r5PiXPaUQmzNHkMllIyhMMBudmgKnjqxnwnSssooQhWpTLOR6UVwc/4ul1lYhx7rwRBF4PBEEwxLFavhkeTmmN2aMEAG3ghdrqHx149z555efx9DGY37vvFNoPcxQe1v0SyObe8cRIYf/4otCPHwUPsVge4yXWgTEumWl9nZtsPp6irbk8OVyTJ5sYyRJdGOjkitJjUP3FyzwNzIOV1ZivdinFG3J4dt5twqW9eJ5nUFKDIB1xNIIh8p6oijUa64RcnyT2SLSJHWXdtqKcdD/BRcIifBiCf44TIGe2WILYaiGhWHFoaFGxnxtvnlWDvwZY/yLF6toy+8LF2rGamrESXK8WRIwe7YcjKYmzdD06Xpi7lzFWqR4/1pYeywMT5ncL4rpiiuE9tgnIRyeozP9ZyVSBNZyskHxpFFUCGroOCf36Zf28MtzRCqxOXPaM24iJxsJQQgOCgyiqM4GJvduKZRbjOWXXH+fPkKKiUQmOnyYRBrwZeeXCIxxQ/mPSfpEkZn3KZO+7PHiH38U2mKurQHY478ooHSSaZswgWOZum5uFra2iqPOTs0g93EE4NzWAKZM4RioIoDx4/PNE4YQxWNTs6HUgL1Pe3sMNCuF9qOVG2penIPHYOHokcJ8+WVhS4sQR4NaJP3F7luI9tSALBFxovhBW6uonFSORm7mzMEAzJjhnHMNDdLI9fXgq68651xdnZLYffpY+cpIhLzkUNNySd98I7QKFPs0cGC+4dv3sCAYIE4bhXJboWvwrruExxwjJPWStyjD8/b0AyEiCt4Wh6xBpCi+225CG5kUdWogxC/HMyldshGJ7IhcWIfTTxciLxhWqKgcJxuPc/h2nTAAaYrq4IP9dni01jCEiVM3nIdWdaizUxuTDdvaSgQQ6kfPNTVhKPgOgP7j5id0Dh9DbR05Unlch54PfQeQRuyrUM7ZOoxkCq6+WkikhiOBIQ/JEx7sGWfE8QlZOYKyOi7sS+uQgdn3ra/4LT/ZSS1razEEfBgoOf0rdRUaIB4rXyCedpqQFAu5KzxdPyBJPPRlj7d1Tw89JLznHiGeb+hDFVIWIQPll9SS3ynKUUwj5RWbI6Q/inWVEgJsUxRFKdKi+T3rLKEtshf9IZytPdgIgNM75PA5dWQ9fBAFE0+cu+dDGQyAHwGMHi0P/sordT12LB4+Ct//0Cbvh1zWINr9gAePIYe4thGCbU//sYRHn/fDLBwyawhDOfe8ESbvCx3/zHuKJp5Cil8cVlVlxcjXhv5yICvh4aPwreDlJY5nUay1Cj2rINn2lD4qPX6KwcPzRQHhKeP5WeQ+ng2nSyhCcv4fKip1EuIXRQlfXIMUn7nPl9SkhKhFFM2v7QcDEIoAQoiHbxV+5V/uEgGQAuJLXRQ7X/KS0sHD///8cpcIl/P69MN3GDh2RBzUvvjLFVI2NuK1einty+CsZDMY/z0qyuOvkGwIY8+zMuE2ZYGHj8JHUWQlm/sm1ENRZ81dWrShLEVAy1/20F9EEZrTQnk9HJCNYL+YLMrD+F/jF7IbnBQQNQDOi/MFJ9fk8O26xisMQmb7OykaagD+p/RLl3JNDj+U0gn13z0xLhQ0BtEqfLtP7TXP057+YvcDCpvDInwvYInUcnIqqjLKK2/IydlnF8NHfvq3FP9/ANgTCJ9z9ZF7AAAAAElFTkSuQmCC) no-repeat top left; + opacity: 0.3; + font-size: 22px; + font-family: Arial; + font-weight: bold; + overflow: hidden; +} +#firechat .icon.plus { + margin-top: 0; + vertical-align: top; + background: transparent; +} +#firechat .icon.search { + background-position: 0 0; + width: 13px; + height: 13px; +} +#firechat .icon.close { + background-position: -120px 0; + width: 13px; + height: 13px; +} +#firechat .icon.user-chat { + background-position: -138px 0; + width: 17px; + height: 13px; +} +#firechat .icon.user-group { + background-position: -18px 0; + width: 17px; + height: 13px; +} +#firechat .icon.user-mute { + background-position: -84px 0; + width: 13px; + height: 13px; +} +#firechat .icon.user-mute.red { + background-position: -102px 0; + width: 13px; + height: 13px; +} +#firechat .icon:hover, +#firechat .btn:hover > .icon { + opacity: 0.6; +} +#firechat a > .icon { + margin: 3px 1px; +} diff --git a/firechat/0.1.2/firechat-default.js b/firechat/0.1.2/firechat-default.js new file mode 100644 index 0000000..e437981 --- /dev/null +++ b/firechat/0.1.2/firechat-default.js @@ -0,0 +1,1830 @@ +(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); +this["FirechatDefaultTemplates"] = this["FirechatDefaultTemplates"] || {}; + +this["FirechatDefaultTemplates"]["templates/layout-full.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
              \n
              \n
              \n\n\nChat Rooms\n\n\n
                \n
                \n\n\nVisitors\n\n\n
                \n
                \n
                \n\n\n
                \n
                \n
                  \n
                  \n
                  \n
                  \n
                  \n
                  \n
                    \n
                    \n
                    \n
                    ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/layout-popout.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                    \n
                    \n
                      \n
                      \n
                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/message-context-menu.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                      \n\n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/message.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                      \n
                      \n'; if (!disableActions) { ;__p += '\n\n'; } ;__p += '
                      \n
                      \n' +((__t = ( message )) == null ? '' : __t) +'\n
                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-alert.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      ' +__e( message ) +'
                      \n

                      \n\n

                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-create-room.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      Give your chat room a name:
                      \n\n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-invitation.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      ' +__e( fromUserName ) +'
                      \n

                      invited you to join

                      \n
                      ' +__e( toRoomName ) +'
                      \n

                      \n\n\n

                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-invite-private.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      Invite ' +__e( userName ) +' to ' +__e( roomName ) +'?
                      \n

                      \n\n\n

                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-invite-reply.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                      \n
                      ' +__e( toUserName ) +'
                      \n

                      \n'; if (status === 'accepted') { ;__p += ' accepted your invite. '; } else { ;__p += ' declined your invite. '; } ;__p += '\n

                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt-user-mute.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      ' +__e( userName ) +'
                      \n

                      \n\n\n

                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/prompt.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      \n' +__e( title ) +'\nX\n
                      \n
                      \n' +((__t = ( content )) == null ? '' : __t) +'\n
                      \n
                      \n
                      ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/room-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                    • \n\n' +__e( name ) +'\n\n
                    • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/room-user-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                    • \n\n' +__e( name ) +''; if (!disableActions) { ;__p += '\n \n \n'; } ;__p += '\n\n
                    • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/room-user-search-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                    • \n\n'; if (disableActions) { ;__p += '\n' +__e( name ) +'\n'; } else { ;__p += '\n' +__e( name ) +'\n+\n'; } ;__p += '\n\n
                    • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/tab-content.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                      \n
                      \n\n\nIn Room\n\n\n
                      \n
                        \n
                        \n
                        \n\n+\nInvite\n\n
                        \n
                        \n
                        \n\n\n
                        \n
                        \n
                          \n
                          \n
                          \n
                          \n
                          \n\n\n
                          \n
                          ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/tab-menu-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p += '
                        • \n' +__e( name ) +'\n
                        • ';}return __p}; + +this["FirechatDefaultTemplates"]["templates/user-search-list-item.html"] = function(obj) {obj || (obj = {});var __t, __p = '', __e = _.escape, __j = Array.prototype.join;function print() { __p += __j.call(arguments, '') }with (obj) {__p += '
                        • \n\n'; if (disableActions) { ;__p += '\n' +__e( name ) +'\n'; } else { ;__p += '\n' +__e( name ) +'\n \n'; } ;__p += '\n\n
                        • ';}return __p}; +(function($) { + + // Shim for Function.bind(...) - (Required by IE < 9, FF < 4, SF < 6) + if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== "function") { + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + return fBound; + }; + } + + // Shim for Object.keys(...) - (Required by IE < 9, FF < 4) + Object.keys = Object.keys || function(oObj) { + var result = []; + for (var name in oObj) { + if (oObj.hasOwnProperty(name)) { + result.push(name); + } + } + return result; + }; + +})(); + +// Firechat is a simple, easily-extensible data layer for multi-user, +// multi-room chat, built entirely on [Firebase](https://firebase.com). +// +// The Firechat object is the primary conduit for all underlying data events. +// It exposes a number of methods for binding event listeners, creating, +// entering, or leaving chat rooms, initiating chats, sending messages, +// and moderator actions such as warning, kicking, or suspending users. +// +// Firechat.js 0.1.0 +// https://firebase.com +// (c) 2013 Firebase +// License: MIT + +// Setup +// -------------- +(function(Firebase) { + + // Establish a reference to the `window` object, and save the previous value + // of the `Firechat` variable. + var root = this, + previousFirechat = root.Firechat; + + function Firechat(firebaseRef, options) { + + // Instantiate a new connection to Firebase. + this._firebase = firebaseRef; + + // User-specific instance variables. + this._user = null; + this._userId = null; + this._userName = null; + this._isModerator = false; + + // A unique id generated for each session. + this._sessionId = null; + + // A mapping of event IDs to an array of callbacks. + this._events = {}; + + // A mapping of room IDs to a boolean indicating presence. + this._rooms = {}; + + // A mapping of operations to re-queue on disconnect. + this._presenceBits = {}; + + // Commonly-used Firebase references. + this._userRef = null; + this._messageRef = this._firebase.child('room-messages'); + this._roomRef = this._firebase.child('room-metadata'); + this._privateRoomRef = this._firebase.child('room-private-metadata'); + this._moderatorsRef = this._firebase.child('moderators'); + this._suspensionsRef = this._firebase.child('suspensions'); + this._usersOnlineRef = this._firebase.child('user-names-online'); + + // Setup and establish default options. + this._options = options || {}; + + // The number of historical messages to load per room. + this._options.numMaxMessages = this._options.numMaxMessages || 50; + } + + // Run Firechat in *noConflict* mode, returning the `Firechat` variable to + // its previous owner, and returning a reference to the Firechat object. + Firechat.noConflict = function noConflict() { + root.Firechat = previousFirechat; + return Firechat; + }; + + // Export the Firechat object as a global. + root.Firechat = Firechat; + + // Firechat Internal Methods + // -------------- + Firechat.prototype = { + + // Load the initial metadata for the user's account and set initial state. + _loadUserMetadata: function(onComplete) { + var self = this; + + // Update the user record with a default name on user's first visit. + this._userRef.transaction(function(current) { + if (!current || !current.id || !current.name) { + return { + id: self._userId, + name: self._userName + }; + } + }, function(error, committed, snapshot) { + self._user = snapshot.val(); + self._moderatorsRef.child(self._userId).once('value', function(snapshot) { + self._isModerator = !!snapshot.val(); + root.setTimeout(onComplete, 0); + }); + }); + }, + + // Initialize Firebase listeners and callbacks for the supported bindings. + _setupDataEvents: function() { + // Monitor connection state so we can requeue disconnect operations if need be. + this._firebase.root().child('.info/connected').on('value', function(snapshot) { + if (snapshot.val() === true) { + // We're connected (or reconnected)! Set up our presence state. + for (var i = 0; i < this._presenceBits; i++) { + var op = this._presenceBits[i], + ref = this._firebase.root().child(op.ref); + + ref.onDisconnect().set(op.offlineValue); + ref.set(op.onlineValue); + } + } + }, this); + + // Generate a unique session id for the visit. + var sessionRef = this._userRef.child('sessions').push(); + this._sessionId = sessionRef.name(); + this._queuePresenceOperation(sessionRef, true, null); + + // Register our username in the public user listing. + var usernameRef = this._usersOnlineRef.child(this._userName.toLowerCase()); + var usernameSessionRef = usernameRef.child(this._sessionId); + this._queuePresenceOperation(usernameSessionRef, { + id: this._userId, + name: this._userName + }, null); + + // Listen for state changes for the given user. + this._userRef.on('value', this._onUpdateUser, this); + + // Listen for chat invitations from other users. + this._userRef.child('invites').on('child_added', this._onFirechatInvite, this); + + // Listen for messages from moderators and adminstrators. + this._userRef.child('notifications').on('child_added', this._onNotification, this); + }, + + // Append the new callback to our list of event handlers. + _addEventCallback: function(eventId, callback) { + this._events[eventId] = this._events[eventId] || []; + this._events[eventId].push(callback); + }, + + // Retrieve the list of event handlers for a given event id. + _getEventCallbacks: function(eventId) { + if (this._events.hasOwnProperty(eventId)) { + return this._events[eventId]; + } + return []; + }, + + // Invoke each of the event handlers for a given event id with specified data. + _invokeEventCallbacks: function(eventId) { + var args = [], + callbacks = this._getEventCallbacks(eventId); + + Array.prototype.push.apply(args, arguments); + args = args.slice(1); + + for (var i = 0; i < callbacks.length; i += 1) { + callbacks[i].apply(null, args); + } + }, + + // Keep track of on-disconnect events so they can be requeued if we disconnect the reconnect. + _queuePresenceOperation: function(ref, onlineValue, offlineValue) { + ref.onDisconnect().set(offlineValue); + ref.set(onlineValue); + this._presenceBits[ref.toString()] = { + ref: ref, + onlineValue: onlineValue, + offlineValue: offlineValue + }; + }, + + // Remove an on-disconnect event from firing upon future disconnect and reconnect. + _removePresenceOperation: function(path, value) { + var ref = new Firebase(path); + ref.onDisconnect().cancel(); + ref.set(value); + delete this._presenceBits[path]; + }, + + // Event to monitor current user state. + _onUpdateUser: function(snapshot) { + this._user = snapshot.val(); + this._invokeEventCallbacks('user-update', this._user); + }, + + // Event to monitor current auth + user state. + _onAuthRequired: function() { + this._invokeEventCallbacks('auth-required'); + }, + + // Events to monitor room entry / exit and messages additional / removal. + _onEnterRoom: function(room) { + this._invokeEventCallbacks('room-enter', room); + }, + _onNewMessage: function(roomId, snapshot) { + var message = snapshot.val(); + message.id = snapshot.name(); + this._invokeEventCallbacks('message-add', roomId, message); + }, + _onRemoveMessage: function(roomId, snapshot) { + var messageId = snapshot.name(); + this._invokeEventCallbacks('message-remove', roomId, messageId); + }, + _onLeaveRoom: function(roomId) { + this._invokeEventCallbacks('room-exit', roomId); + }, + + // Event to listen for notifications from administrators and moderators. + _onNotification: function(snapshot) { + var notification = snapshot.val(); + if (!notification.read) { + if (notification.notificationType !== 'suspension' || notification.data.suspendedUntil < Firebase.ServerValue.TIMESTAMP) { + snapshot.ref().child('read').set(true); + } + this._invokeEventCallbacks('notification', notification); + } + }, + + // Events to monitor chat invitations and invitation replies. + _onFirechatInvite: function(snapshot) { + var self = this, + invite = snapshot.val(); + + // Skip invites we've already responded to. + if (invite.status) { + return; + } + + invite.id = invite.id || snapshot.name(); + self.getRoom(invite.roomId, function(room) { + invite.toRoomName = room.name; + self._invokeEventCallbacks('room-invite', invite); + }); + }, + _onFirechatInviteResponse: function(snapshot) { + var self = this, + invite = snapshot.val(); + + invite.id = invite.id || snapshot.name(); + this._invokeEventCallbacks('room-invite-response', invite); + } + }; + + // Firechat External Methods + // -------------- + + // Initialize the library and setup data listeners. + Firechat.prototype.setUser = function(userId, userName, callback) { + var self = this; + + self._firebase.root().child('.info/authenticated').on('value', function(snapshot) { + if (snapshot.val() === true) { + self._firebase.root().child('.info/authenticated').off(); + + self._userId = userId.toString(); + self._userName = userName.toString(); + self._userRef = self._firebase.child('users').child(self._userId); + self._loadUserMetadata(function() { + root.setTimeout(function() { + callback(self._user); + self._setupDataEvents(); + }, 0); + }); + } + }); + }; + + // Resumes the previous session by automatically entering rooms. + Firechat.prototype.resumeSession = function() { + this._userRef.child('rooms').once('value', function(snapshot) { + var rooms = snapshot.val(); + for (var roomId in rooms) { + this.enterRoom(rooms[roomId].id); + } + }, /* onError */ function(){}, /* context */ this); + }; + + // Callback registration. Supports each of the following events: + Firechat.prototype.on = function(eventType, cb) { + this._addEventCallback(eventType, cb); + }; + + // Create and automatically enter a new chat room. + Firechat.prototype.createRoom = function(roomName, roomType, callback) { + var self = this, + newRoomRef = this._roomRef.push(); + + var newRoom = { + id: newRoomRef.name(), + name: roomName, + type: roomType || 'public', + createdByUserId: this._userId, + createdAt: Firebase.ServerValue.TIMESTAMP + }; + + if (roomType === 'private') { + newRoom.authorizedUsers = {}; + newRoom.authorizedUsers[this._userId] = true; + } + + newRoomRef.set(newRoom, function(error) { + if (!error) { + self.enterRoom(newRoomRef.name()); + } + if (callback) { + callback(newRoomRef.name()); + } + }); + }; + + // Enter a chat room. + Firechat.prototype.enterRoom = function(roomId) { + var self = this; + self.getRoom(roomId, function(room) { + var roomName = room.name; + + if (!roomId || !roomName) return; + + // Skip if we're already in this room. + if (self._rooms[roomId]) { + return; + } + + self._rooms[roomId] = true; + + if (self._user) { + // Save entering this room to resume the session again later. + self._userRef.child('rooms').child(roomId).set({ + id: roomId, + name: roomName, + active: true + }); + + // Set presence bit for the room and queue it for removal on disconnect. + var presenceRef = self._firebase.child('room-users').child(roomId).child(self._userId).child(self._sessionId); + self._queuePresenceOperation(presenceRef, { + id: self._userId, + name: self._userName + }, null); + } + + // Invoke our callbacks before we start listening for new messages. + self._onEnterRoom({ id: roomId, name: roomName }); + + // Setup message listeners + self._roomRef.child(roomId).once('value', function(snapshot) { + self._messageRef.child(roomId).limit(self._options.numMaxMessages).on('child_added', function(snapshot) { + self._onNewMessage(roomId, snapshot); + }, /* onCancel */ function() { + // Turns out we don't have permission to access these messages. + self.leaveRoom(roomId); + }, /* context */ self); + + self._messageRef.child(roomId).limit(self._options.numMaxMessages).on('child_removed', function(snapshot) { + self._onRemoveMessage(roomId, snapshot); + }, /* onCancel */ function(){}, /* context */ self); + }, /* onFailure */ function(){}, self); + }); + }; + + // Leave a chat room. + Firechat.prototype.leaveRoom = function(roomId) { + var self = this, + userRoomRef = self._firebase.child('room-users').child(roomId), + presenceRef = userRoomRef.child(self._userId).child(self._sessionId); + + // Remove listener for new messages to this room. + self._messageRef.child(roomId).off(); + + if (self._user) { + // Remove presence bit for the room and cancel on-disconnect removal. + self._removePresenceOperation(presenceRef.toString(), null); + + // Remove session bit for the room. + self._userRef.child('rooms').child(roomId).remove(); + } + + delete self._rooms[roomId]; + + // Invoke event callbacks for the room-exit event. + self._onLeaveRoom(roomId); + }; + + Firechat.prototype.sendMessage = function(roomId, messageContent, messageType, cb) { + var self = this, + message = { + userId: self._userId, + name: self._userName, + timestamp: Firebase.ServerValue.TIMESTAMP, + message: messageContent, + type: messageType || 'default' + }, + newMessageRef; + + if (!self._user) { + self._onAuthRequired(); + if (cb) { + cb(new Error('Not authenticated or user not set!')); + } + return; + } + + newMessageRef = self._messageRef.child(roomId).push(); + newMessageRef.setWithPriority(message, Firebase.ServerValue.TIMESTAMP, cb); + }; + + Firechat.prototype.deleteMessage = function(roomId, messageId, cb) { + var self = this; + + self._messageRef.child(roomId).child(messageId).remove(cb); + }; + + // Mute or unmute a given user by id. This list will be stored internally and + // all messages from the muted clients will be filtered client-side after + // receipt of each new message. + Firechat.prototype.toggleUserMute = function(userId, cb) { + var self = this; + + if (!self._user) { + self._onAuthRequired(); + if (cb) { + cb(new Error('Not authenticated or user not set!')); + } + return; + } + + self._userRef.child('muted').child(userId).transaction(function(isMuted) { + return (isMuted) ? null : true; + }, cb); + }; + + // Send a moderator notification to a specific user. + Firechat.prototype.sendSuperuserNotification = function(userId, notificationType, data, cb) { + var self = this, + userNotificationsRef = self._firebase.child('users').child(userId).child('notifications'); + + userNotificationsRef.push({ + fromUserId: self._userId, + timestamp: Firebase.ServerValue.TIMESTAMP, + notificationType: notificationType, + data: data || {} + }, cb); + }; + + // Warn a user for violating the terms of service or being abusive. + Firechat.prototype.warnUser = function(userId) { + var self = this; + + self.sendSuperuserNotification(userId, 'warning'); + }; + + // Suspend a user by putting the user into read-only mode for a period. + Firechat.prototype.suspendUser = function(userId, timeLengthSeconds, cb) { + var self = this, + suspendedUntil = Firebase.ServerValue.TIMESTAMP + 1000*timeLengthSeconds; + + self._suspensionsRef.child(userId).set(suspendedUntil, function(error) { + if (error && cb) { + return cb(error); + } else { + self.sendSuperuserNotification(userId, 'suspension', { + suspendedUntil: suspendedUntil + }); + return cb(null); + } + }); + }; + + // Invite a user to a specific chat room. + Firechat.prototype.inviteUser = function(userId, roomId) { + var self = this, + sendInvite = function() { + var inviteRef = self._firebase.child('users').child(userId).child('invites').push(); + inviteRef.set({ + id: inviteRef.name(), + fromUserId: self._userId, + fromUserName: self._userName, + roomId: roomId + }); + + // Handle listen unauth / failure in case we're kicked. + inviteRef.on('value', self._onFirechatInviteResponse, function(){}, self); + }; + + if (!self._user) { + self._onAuthRequired(); + return; + } + + self.getRoom(roomId, function(room) { + if (room.type === 'private') { + var authorizedUserRef = self._roomRef.child(roomId).child('authorizedUsers'); + authorizedUserRef.child(userId).set(true, function(error) { + if (!error) { + sendInvite(); + } + }); + } else { + sendInvite(); + } + }); + }; + + Firechat.prototype.acceptInvite = function(inviteId, cb) { + var self = this; + + self._userRef.child('invites').child(inviteId).once('value', function(snapshot) { + var invite = snapshot.val(); + if (invite === null && cb) { + return cb(new Error('acceptInvite(' + inviteId + '): invalid invite id')); + } else { + self.enterRoom(invite.roomId); + self._userRef.child('invites').child(inviteId).update({ + 'status': 'accepted', + 'toUserName': self._userName + }, cb); + } + }, self); + }; + + Firechat.prototype.declineInvite = function(inviteId, cb) { + var self = this, + updates = { + 'status': 'declined', + 'toUserName': self._userName + }; + + self._userRef.child('invites').child(inviteId).update(updates, cb); + }; + + Firechat.prototype.getRoomList = function(cb) { + var self = this; + + self._roomRef.once('value', function(snapshot) { + cb(snapshot.val()); + }); + }; + + Firechat.prototype.getUsersByRoom = function() { + var self = this, + roomId = arguments[0], + query = self._firebase.child('room-users').child(roomId), + cb = arguments[arguments.length - 1], + limit = null; + + if (arguments.length > 2) { + limit = arguments[1]; + } + + query = (limit) ? query.limit(limit) : query; + + query.once('value', function(snapshot) { + var usernames = snapshot.val() || {}, + usernamesUnique = {}; + + for (var username in usernames) { + for (var session in usernames[username]) { + // Skip all other sessions for this user as we only need one. + usernamesUnique[username] = usernames[username][session]; + break; + } + } + + root.setTimeout(function() { + cb(usernamesUnique); + }, 0); + }); + }; + + Firechat.prototype.getUsersByPrefix = function(prefix, startAt, endAt, limit, cb) { + var self = this, + query = this._usersOnlineRef, + prefixLower = prefix.toLowerCase(); + + if (startAt) { + query = query.startAt(null, startAt); + } else if (endAt) { + query = query.endAt(null, endAt); + } else { + query = (prefixLower) ? query.startAt(null, prefixLower) : query.startAt(); + } + + query = (limit) ? query.limit(limit) : query; + + query.once('value', function(snapshot) { + var usernames = snapshot.val() || {}, + usernamesFiltered = {}; + + for (var userNameKey in usernames) { + var sessions = usernames[userNameKey], + userName, userId, usernameClean; + + // Grab the user data from the first registered active session. + for (var sessionId in sessions) { + userName = sessions[sessionId].name; + userId = sessions[sessionId].id; + + // Skip all other sessions for this user as we only need one. + break; + } + + // Filter out any usernames that don't match our prefix and break. + if ((prefix.length > 0) && (userName.toLowerCase().indexOf(prefixLower) !== 0)) + continue; + + usernamesFiltered[userName] = { + name: userName, + id: userId + }; + } + + root.setTimeout(function() { + cb(usernamesFiltered); + }, 0); + }); + }; + + // Miscellaneous helper methods. + Firechat.prototype.getRoom = function(roomId, callback) { + this._roomRef.child(roomId).once('value', function(snapshot) { + callback(snapshot.val()); + }); + }; + + Firechat.prototype.userIsModerator = function() { + return this._isModerator; + }; +})(Firebase); +(function($) { + + + if (!$ || (parseInt($().jquery.replace(/\./g, ""), 10) < 170)) { + throw new Error("jQuery 1.7 or later required!"); + } + + var root = this, + previousFirechatUI = root.FirechatUI; + + root.FirechatUI = FirechatUI; + + if (!self.FirechatDefaultTemplates) { + throw new Error("Unable to find chat templates!"); + } + + function FirechatUI(firebaseRef, el, options) { + var self = this; + + if (!firebaseRef) { + throw new Error('FirechatUI: Missing required argument `firebaseRef`'); + } + + if (!el) { + throw new Error('FirechatUI: Missing required argument `el`'); + } + + options = options || {}; + this._options = options; + + this._el = el; + this._user = null; + this._chat = new Firechat(firebaseRef, options); + + // A list of rooms to enter once we've made room for them (once we've hit the max room limit). + this._roomQueue = []; + + // Define some constants regarding maximum lengths, client-enforced. + this.maxLengthUsername = 15; + this.maxLengthUsernameDisplay = 15; + this.maxLengthRoomName = 24; + this.maxLengthMessage = 120; + this.maxUserSearchResults = 100; + + // Define some useful regexes. + this.urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim; + this.pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim; + + this._renderLayout(); + + // Grab shortcuts to commonly used jQuery elements. + this.$wrapper = $('#firechat'); + this.$roomList = $('#firechat-room-list'); + this.$tabList = $('#firechat-tab-list'); + this.$tabContent = $('#firechat-tab-content'); + this.$messages = {}; + + // Rate limit messages from a given user with some defaults. + this.$rateLimit = { + limitCount: 10, // max number of events + limitInterval: 10000, // max interval for above count in milliseconds + limitWaitTime: 30000, // wait time if a user hits the wait limit + history: {} + }; + + // Setup UI bindings for chat controls. + this._bindUIEvents(); + + // Setup bindings to internal methods + this._bindDataEvents(); + } + + // Run FirechatUI in *noConflict* mode, returning the `FirechatUI` variable to + // its previous owner, and returning a reference to the FirechatUI object. + FirechatUI.noConflict = function noConflict() { + root.FirechatUI = previousFirechatUI; + return FirechatUI; + }; + + FirechatUI.prototype = { + + _bindUIEvents: function() { + // Chat-specific custom interactions and functionality. + this._bindForHeightChange(); + this._bindForTabControls(); + this._bindForRoomList(); + this._bindForUserRoomList(); + this._bindForUserSearch(); + this._bindForUserMuting(); + this._bindForChatInvites(); + this._bindForRoomListing(); + + // Generic, non-chat-specific interactive elements. + this._setupTabs(); + this._setupDropdowns(); + this._bindTextInputFieldLimits(); + }, + + _bindDataEvents: function() { + this._chat.on('user-update', this._onUpdateUser.bind(this)); + + // Bind events for new messages, enter / leaving rooms, and user metadata. + this._chat.on('room-enter', this._onEnterRoom.bind(this)); + this._chat.on('room-exit', this._onLeaveRoom.bind(this)); + this._chat.on('message-add', this._onNewMessage.bind(this)); + this._chat.on('message-remove', this._onRemoveMessage.bind(this)); + + // Bind events related to chat invitations. + this._chat.on('room-invite', this._onChatInvite.bind(this)); + this._chat.on('room-invite-response', this._onChatInviteResponse.bind(this)); + + // Binds events related to admin or moderator notifications. + this._chat.on('notification', this._onNotification.bind(this)); + }, + + _renderLayout: function() { + var template = FirechatDefaultTemplates["templates/layout-full.html"]; + $(this._el).html(template({ + maxLengthUsername: this.maxLengthUsername + })); + }, + + _onUpdateUser: function(user) { + // Update our current user state and render latest user name. + this._user = user; + + // Update our interface to reflect which users are muted or not. + var mutedUsers = this._user.muted || {}; + $('[data-event="firechat-user-mute-toggle"]').each(function(i, el) { + var userId = $(this).closest('[data-user-id]').data('user-id'); + $(this).toggleClass('red', !!mutedUsers[userId]); + }); + + // Ensure that all messages from muted users are removed. + for (var userId in mutedUsers) { + $('.message[data-user-id="' + userId + '"]').fadeOut(); + } + }, + + _onEnterRoom: function(room) { + this.attachTab(room.id, room.name); + }, + _onLeaveRoom: function(roomId) { + this.removeTab(roomId); + + // Auto-enter rooms in the queue + if ((this._roomQueue.length > 0)) { + this._chat.enterRoom(this._roomQueue.shift(roomId)); + } + }, + _onNewMessage: function(roomId, message) { + var userId = message.userId; + if (!this._user || !this._user.muted || !this._user.muted[userId]) { + this.showMessage(roomId, message); + } + }, + _onRemoveMessage: function(roomId, messageId) { + this.removeMessage(roomId, messageId); + }, + + // Events related to chat invitations. + _onChatInvite: function(invitation) { + var self = this; + var template = FirechatDefaultTemplates["templates/prompt-invitation.html"]; + var $prompt = this.prompt('Invite', template(invitation)); + $prompt.find('a.close').click(function() { + $prompt.remove(); + self._chat.declineInvite(invitation.id); + }); + + $prompt.find('[data-toggle=accept]').click(function() { + $prompt.remove(); + self._chat.acceptInvite(invitation.id); + }); + + $prompt.find('[data-toggle=decline]').click(function() { + $prompt.remove(); + self._chat.declineInvite(invitation.id); + }); + }, + _onChatInviteResponse: function(invitation) { + if (!invitation.status) return; + + var self = this, + template = FirechatDefaultTemplates["templates/prompt-invite-reply.html"], + $prompt; + + if (invitation.status && invitation.status === 'accepted') { + $prompt = this.prompt('Accepted', template(invitation)); + this._chat.getRoom(invitation.toRoomId, function(room) { + self.attachTab(invitation.toRoomId, room.name); + }); + } else { + $prompt = this.prompt('Declined', template(invitation)); + } + + $prompt.find('a.close').click(function() { + $prompt.remove(); + }); + }, + + // Events related to admin or moderator notifications. + _onNotification: function(notification) { + if (notification.notificationType === 'warning') { + this.renderAlertPrompt('Warning', 'You are being warned for inappropriate messaging. Further violation may result in temporary or permanent ban of service.'); + } else if (notification.notificationType === 'suspension') { + var suspendedUntil = notification.data.suspendedUntil, + secondsLeft = Math.round((suspendedUntil - new Date().getTime()) / 1000), + timeLeft = ''; + + if (secondsLeft > 0) { + if (secondsLeft > 2*3600) { + var hours = Math.floor(secondsLeft / 3600); + timeLeft = hours + ' hours, '; + secondsLeft -= 3600*hours; + } + timeLeft += Math.floor(secondsLeft / 60) + ' minutes'; + this.renderAlertPrompt('Suspended', 'A moderator has suspended you for violating site rules. You cannot send messages for another ' + timeLeft + '.'); + } + } + } + }; + + /** + * Initialize an authenticated session with a user id and name. + * This method assumes that the underlying Firebase reference has + * already been authenticated. + */ + FirechatUI.prototype.setUser = function(userId, userName) { + var self = this; + + // Initialize data events + self._chat.setUser(userId, userName, function(user) { + self._user = user; + + if (self._chat.userIsModerator()) { + self._bindSuperuserUIEvents(); + } + + self._chat.resumeSession(); + }); + }; + + /** + * Exposes internal chat bindings via this external interface. + */ + FirechatUI.prototype.on = function(eventType, cb) { + var self = this; + + this._chat.on(eventType, cb); + }; + + /** + * Binds a custom context menu to messages for superusers to warn or ban + * users for violating terms of service. + */ + FirechatUI.prototype._bindSuperuserUIEvents = function() { + var self = this, + parseMessageVars = function(event) { + var $this = $(this), + messageId = $this.closest('[data-message-id]').data('message-id'), + userId = $('[data-message-id="' + messageId + '"]').closest('[data-user-id]').data('user-id'), + roomId = $('[data-message-id="' + messageId + '"]').closest('[data-room-id]').data('room-id'); + + return { messageId: messageId, userId: userId, roomId: roomId }; + }, + clearMessageContextMenus = function() { + // Remove any context menus currently showing. + $('[data-toggle="firechat-contextmenu"]').each(function() { + $(this).remove(); + }); + + // Remove any messages currently highlighted. + $('#firechat .message.highlighted').each(function() { + $(this).removeClass('highlighted'); + }); + }, + showMessageContextMenu = function(event) { + var $this = $(this), + $message = $this.closest('[data-message-id]'), + template = FirechatDefaultTemplates["templates/message-context-menu.html"], + messageVars = parseMessageVars.call(this, event), + $template; + + event.preventDefault(); + + // Clear existing menus. + clearMessageContextMenus(); + + // Highlight the relevant message. + $this.addClass('highlighted'); + + self._chat.getRoom(messageVars.roomId, function(room) { + // Show the context menu. + $template = $(template({ + id: $message.data('message-id') + })); + $template.css({ + left: event.clientX, + top: event.clientY + }).appendTo(self.$wrapper); + }); + }; + + // Handle dismissal of message context menus (any non-right-click click event). + $(document).bind('click', { self: this }, function(event) { + if (!event.button || event.button != 2) { + clearMessageContextMenus(); + } + }); + + // Handle display of message context menus (via right-click on a message). + $(document).delegate('[data-class="firechat-message"]', 'contextmenu', showMessageContextMenu); + + // Handle click of the 'Warn User' contextmenu item. + $(document).delegate('[data-event="firechat-user-warn"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.warnUser(messageVars.userId); + }); + + // Handle click of the 'Suspend User (1 Hour)' contextmenu item. + $(document).delegate('[data-event="firechat-user-suspend-hour"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.suspendUser(messageVars.userId, /* 1 Hour = 3600s */ 60*60); + }); + + // Handle click of the 'Suspend User (1 Day)' contextmenu item. + $(document).delegate('[data-event="firechat-user-suspend-day"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.suspendUser(messageVars.userId, /* 1 Day = 86400s */ 24*60*60); + }); + + // Handle click of the 'Delete Message' contextmenu item. + $(document).delegate('[data-event="firechat-message-delete"]', 'click', function(event) { + var messageVars = parseMessageVars.call(this, event); + self._chat.deleteMessage(messageVars.roomId, messageVars.messageId); + }); + }; + + /** + * Binds to height changes in the surrounding div. + */ + FirechatUI.prototype._bindForHeightChange = function() { + var self = this, + $el = $(this._el), + lastHeight = null; + + setInterval(function() { + var height = $el.height(); + if (height != lastHeight) { + lastHeight = height; + $('.chat').each(function(i, el) { + + }); + } + }, 500); + }; + + /** + * Binds custom inner-tab events. + */ + FirechatUI.prototype._bindForTabControls = function() { + var self = this; + + // Handle click of tab close button. + $(document).delegate('[data-event="firechat-close-tab"]', 'click', function(event) { + var roomId = $(this).closest('[data-room-id]').data('room-id'); + self._chat.leaveRoom(roomId); + }); + }; + + /** + * Binds room list dropdown to populate room list on-demand. + */ + FirechatUI.prototype._bindForRoomList = function() { + var self = this; + + $('#firechat-btn-rooms').bind('click', function() { + if ($(this).parent().hasClass('open')) { + return; + } + + var $this = $(this), + template = FirechatDefaultTemplates["templates/room-list-item.html"], + selectRoomListItem = function() { + var parent = $(this).parent(), + roomId = parent.data('room-id'), + roomName = parent.data('room-name'); + + if (self.$messages[roomId]) { + self.focusTab(roomId); + } else { + self._chat.enterRoom(roomId, roomName); + } + }; + + self._chat.getRoomList(function(rooms) { + self.$roomList.empty(); + for (var roomId in rooms) { + var room = rooms[roomId]; + if (room.type != "public") continue; + room.isRoomOpen = !!self.$messages[room.id]; + var $roomItem = $(template(room)); + $roomItem.children('a').bind('click', selectRoomListItem); + self.$roomList.append($roomItem.toggle(true)); + } + }); + }); + }; + + /** + * Binds user list dropdown per room to populate user list on-demand. + */ + FirechatUI.prototype._bindForUserRoomList = function() { + var self = this; + + // Upon click of the dropdown, autofocus the input field and trigger list population. + $(document).delegate('[data-event="firechat-user-room-list-btn"]', 'click', function(event) { + event.stopPropagation(); + + var $this = $(this), + roomId = $this.closest('[data-room-id]').data('room-id'), + template = FirechatDefaultTemplates["templates/room-user-list-item.html"], + targetId = $this.data('target'), + $target = $('#' + targetId); + + $target.empty(); + self._chat.getUsersByRoom(roomId, function(users) { + for (var username in users) { + user = users[username]; + user.disableActions = (!self._user || user.id === self._user.id); + user.nameTrimmed = self.trimWithEllipsis(user.name, self.maxLengthUsernameDisplay); + user.isMuted = (self._user && self._user.muted && self._user.muted[user.id]); + $target.append($(template(user))); + } + self.sortListLexicographically('#' + targetId); + }); + }); + }; + + /** + * Binds user search buttons, dropdowns, and input fields for searching all + * active users currently in chat. + */ + FirechatUI.prototype._bindForUserSearch = function() { + var self = this, + handleUserSearchSubmit = function(event) { + var $this = $(this), + targetId = $this.data('target'), + controlsId = $this.data('controls'), + templateId = $this.data('template'), + prefix = $this.val() || $this.data('prefix') || '', + startAt = $this.data('startAt') || null, + endAt = $this.data('endAt') || null; + + event.preventDefault(); + + userSearch(targetId, templateId, controlsId, prefix, startAt, endAt); + }, + userSearch = function(targetId, templateId, controlsId, prefix, startAt, endAt) { + var $target = $('#' + targetId), + $controls = $('#' + controlsId), + template = FirechatDefaultTemplates[templateId]; + + // Query results, filtered by prefix, using the defined startAt and endAt markets. + self._chat.getUsersByPrefix(prefix, startAt, endAt, self.maxUserSearchResults, function(users) { + var numResults = 0, + $prevBtn, $nextBtn, username, firstResult, lastResult; + + $target.empty(); + + for (username in users) { + var user = users[username]; + + // Disable buttons for . + user.disableActions = (!self._user || user.id === self._user.id); + + numResults += 1; + + $target.append(template(user)); + + // If we've hit our result limit, the additional value signifies we should paginate. + if (numResults === 1) { + firstResult = user.name.toLowerCase(); + } else if (numResults >= self.maxUserSearchResults) { + lastResult = user.name.toLowerCase(); + break; + } + } + + if ($controls) { + $prevBtn = $controls.find('[data-toggle="firechat-pagination-prev"]'); + $nextBtn = $controls.find('[data-toggle="firechat-pagination-next"]'); + + // Sort out configuration for the 'next' button + if (lastResult) { + $nextBtn + .data('event', 'firechat-user-search') + .data('startAt', lastResult) + .data('prefix', prefix) + .removeClass('disabled').removeAttr('disabled'); + } else { + $nextBtn + .data('event', null) + .data('startAt', null) + .data('prefix', null) + .addClass('disabled').attr('disabled', 'disabled'); + } + } + }); + }; + + $(document).delegate('[data-event="firechat-user-search"]', 'keyup', handleUserSearchSubmit); + $(document).delegate('[data-event="firechat-user-search"]', 'click', handleUserSearchSubmit); + + // Upon click of the dropdown, autofocus the input field and trigger list population. + $(document).delegate('[data-event="firechat-user-search-btn"]', 'click', function(event) { + event.stopPropagation(); + var $input = $(this).next('div.dropdown-menu').find('input'); + $input.focus(); + $input.trigger(jQuery.Event('keyup')); + }); + + // Ensure that the dropdown stays open despite clicking on the input element. + $(document).delegate('[data-event="firechat-user-search"]', 'click', function(event) { + event.stopPropagation(); + }); + }; + + /** + * Binds user mute toggles and removes all messages for a given user upon mute. + */ + FirechatUI.prototype._bindForUserMuting = function() { + var self = this; + $(document).delegate('[data-event="firechat-user-mute-toggle"]', 'click', function(event) { + var $this = $(this), + userId = $this.closest('[data-user-id]').data('user-id'), + userName = $this.closest('[data-user-name]').data('user-name'), + isMuted = $this.hasClass('red'), + template = FirechatDefaultTemplates["templates/prompt-user-mute.html"]; + + event.preventDefault(); + + // Require user confirmation for muting. + if (!isMuted) { + var $prompt = self.prompt('Mute User?', template({ + userName: userName + })); + + $prompt.find('a.close').first().click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=decline]').first().click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=accept]').first().click(function() { + self._chat.toggleUserMute(userId); + $prompt.remove(); + }); + } else { + self._chat.toggleUserMute(userId); + } + }); + }; + + /** + * Binds to elements with the data-event='firechat-user-(private)-invite' and + * handles invitations as well as room creation and entering. + */ + FirechatUI.prototype._bindForChatInvites = function() { + var self = this, + renderInvitePrompt = function(event) { + var $this = $(this), + userId = $this.closest('[data-user-id]').data('user-id'), + roomId = $this.closest('[data-room-id]').data('room-id'), + userName = $this.closest('[data-user-name]').data('user-name'), + template = FirechatDefaultTemplates["templates/prompt-invite-private.html"], + $prompt; + + self._chat.getRoom(roomId, function(room) { + $prompt = self.prompt('Invite', template({ + userName: userName, + roomName: room.name + })); + + $prompt.find('a.close').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=decline]').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=accept]').first().click(function() { + $prompt.remove(); + self._chat.inviteUser(userId, roomId, room.name); + }); + }); + }, + renderPrivateInvitePrompt = function(event) { + var $this = $(this), + userId = $this.closest('[data-user-id]').data('user-id'), + userName = $this.closest('[data-user-name]').data('user-name'), + template = FirechatDefaultTemplates["templates/prompt-invite-private.html"], + $prompt; + + if (userId && userName) { + $prompt = self.prompt('Private Invite', template({ + userName: userName, + roomName: 'Private Chat' + })); + + $prompt.find('a.close').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=decline]').click(function() { + $prompt.remove(); + }); + + $prompt.find('[data-toggle=accept]').first().click(function() { + $prompt.remove(); + var roomName = 'Private Chat'; + self._chat.createRoom(roomName, 'private', function(roomId) { + self._chat.inviteUser(userId, roomId, roomName); + }); + }); + } + }; + + $(document).delegate('[data-event="firechat-user-chat"]', 'click', renderPrivateInvitePrompt); + $(document).delegate('[data-event="firechat-user-invite"]', 'click', renderInvitePrompt); + }; + + /** + * Binds to room dropdown button, menu items, and create room button. + */ + FirechatUI.prototype._bindForRoomListing = function() { + var self = this, + $createRoomPromptButton = $('#firechat-btn-create-room-prompt'), + $createRoomButton = $('#firechat-btn-create-room'), + renderRoomList = function(event) { + var type = $(this).data('room-type'); + + self.sortListLexicographically('#firechat-room-list'); + }; + + // Handle click of the create new room prompt-button. + $createRoomPromptButton.bind('click', function(event) { + self.promptCreateRoom(); + }); + + // Handle click of the create new room button. + $createRoomButton.bind('click', function(event) { + var roomName = $('#firechat-input-room-name').val(); + $('#firechat-prompt-create-room').remove(); + self._chat.createRoom(roomName); + }); + }; + + /** + * A stripped-down version of bootstrap-tab.js. + * + * Original bootstrap-tab.js Copyright 2012 Twitter, Inc.,licensed under the Apache v2.0 + */ + FirechatUI.prototype._setupTabs = function() { + var self = this, + show = function($el) { + var $this = $el, + $ul = $this.closest('ul:not(.dropdown-menu)'), + selector = $this.attr('data-target'), + previous = $ul.find('.active:last a')[0], + $target, + e; + + if (!selector) { + selector = $this.attr('href'); + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); + } + + if ($this.parent('li').hasClass('active')) return; + + e = $.Event('show', { relatedTarget: previous }); + + $this.trigger(e); + + if (e.isDefaultPrevented()) return; + + $target = $(selector); + + activate($this.parent('li'), $ul); + activate($target, $target.parent(), function () { + $this.trigger({ + type: 'shown', + relatedTarget: previous + }); + }); + }, + activate = function (element, container, callback) { + var $active = container.find('> .active'), + transition = callback && $.support.transition && $active.hasClass('fade'); + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active'); + + element.addClass('active'); + + if (transition) { + element.addClass('in'); + } else { + element.removeClass('fade'); + } + + if (element.parent('.dropdown-menu')) { + element.closest('li.dropdown').addClass('active'); + } + + if (callback) { + callback(); + } + } + + if (transition) { + $active.one($.support.transition.end, next); + } else { + next(); + } + + $active.removeClass('in'); + }; + + $(document).delegate('[data-toggle="tab"]', 'click', function(event) { + event.preventDefault(); + show($(this)); + }); + }; + + /** + * A stripped-down version of bootstrap-dropdown.js. + * + * Original bootstrap-dropdown.js Copyright 2012 Twitter, Inc., licensed under the Apache v2.0 + */ + FirechatUI.prototype._setupDropdowns = function() { + var self = this, + toggle = '[data-toggle=dropdown]', + toggleDropdown = function(event) { + var $this = $(this), + $parent = getParent($this), + isActive = $parent.hasClass('open'); + + if ($this.is('.disabled, :disabled')) return; + + clearMenus(); + + if (!isActive) { + $parent.toggleClass('open'); + } + + $this.focus(); + + return false; + }, + clearMenus = function() { + $('[data-toggle=dropdown]').each(function() { + getParent($(this)).removeClass('open'); + }); + }, + getParent = function($this) { + var selector = $this.attr('data-target'), + $parent; + + if (!selector) { + selector = $this.attr('href'); + selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); + } + + $parent = selector && $(selector); + + if (!$parent || !$parent.length) $parent = $this.parent(); + + return $parent; + }; + + $(document) + .bind('click', clearMenus) + .delegate('.dropdown-menu', 'click', function(event) { event.stopPropagation(); }) + .delegate('[data-toggle=dropdown]', 'click', toggleDropdown); + }; + + /** + * Binds to any text input fields with data-provide='limit' and + * data-counter='', and upon value change updates the selector + * content to reflect the number of characters remaining, as the 'maxlength' + * attribute less the current value length. + */ + FirechatUI.prototype._bindTextInputFieldLimits = function() { + $('body').delegate('input[data-provide="limit"], textarea[data-provide="limit"]', 'keyup', function(event) { + var $this = $(this), + $target = $($this.data('counter')), + limit = $this.attr('maxlength'), + count = $this.val().length; + + $target.html(Math.max(0, limit - count)); + }); + }; + + /** + * Given a title and message content, show an alert prompt to the user. + * + * @param {string} title + * @param {string} message + */ + FirechatUI.prototype.renderAlertPrompt = function(title, message) { + var template = FirechatDefaultTemplates["templates/prompt-alert.html"], + $prompt = this.prompt(title, template({ message: message })); + + $prompt.find('.close').click(function() { + $prompt.remove(); + }); + return; + }; + + /** + * Toggle input field s if we want limit / unlimit input fields. + */ + FirechatUI.prototype.toggleInputs = function(isEnabled) { + $('#firechat-tab-content textarea').each(function() { + var $this = $(this); + if (isEnabled) { + $(this).val(''); + } else { + $(this).val('You have exceeded the message limit, please wait before sending.'); + } + $this.prop('disabled', !isEnabled); + }); + $('#firechat-input-name').prop('disabled', !isEnabled); + }; + + /** + * Given a room id and name, attach the tab to the interface and setup events. + * + * @param {string} roomId + * @param {string} roomName + */ + FirechatUI.prototype.attachTab = function(roomId, roomName) { + var self = this; + + // If this tab already exists, give it focus. + if (this.$messages[roomId]) { + this.focusTab(roomId); + return; + } + + var room = { + id: roomId, + name: roomName + }; + + // Populate and render the tab content template. + var tabTemplate = FirechatDefaultTemplates["templates/tab-content.html"]; + var $tabContent = $(tabTemplate(room)); + this.$tabContent.prepend($tabContent); + var $messages = $('#firechat-messages' + roomId); + + // Keep a reference to the message listing for later use. + this.$messages[roomId] = $messages; + + // Attach on-enter event to textarea. + var $textarea = $tabContent.find('textarea').first(); + $textarea.bind('keydown', function(e) { + var message = self.trimWithEllipsis($textarea.val(), self.maxLengthMessage); + if ((e.which === 13) && (message !== '')) { + $textarea.val(''); + self._chat.sendMessage(roomId, message); + return false; + } + }); + + // Populate and render the tab menu template. + var tabListTemplate = FirechatDefaultTemplates["templates/tab-menu-item.html"]; + var $tab = $(tabListTemplate(room)); + this.$tabList.prepend($tab); + + // Attach on-shown event to move tab to front and scroll to bottom. + $tab.bind('shown', function(event) { + $messages.scrollTop($messages[0].scrollHeight); + }); + + // Dynamically update the width of each tab based upon the number open. + var tabs = this.$tabList.children('li'); + var tabWidth = Math.floor($('#firechat-tab-list').width() / tabs.length); + this.$tabList.children('li').css('width', tabWidth); + + // Update the room listing to reflect that we're now in the room. + this.$roomList.children('[data-room-id=' + roomId + ']').children('a').addClass('highlight'); + + // Sort each item in the user list alphabetically on click of the dropdown. + $('#firechat-btn-room-user-list-' + roomId).bind('click', function() { + self.sortListLexicographically('#firechat-room-user-list-' + roomId); + }); + + // Automatically select the new tab. + this.focusTab(roomId); + }; + + /** + * Given a room id, focus the given tab. + * + * @param {string} roomId + */ + FirechatUI.prototype.focusTab = function(roomId) { + if (this.$messages[roomId]) { + var $tabLink = this.$tabList.find('[data-room-id=' + roomId + ']').find('a'); + if ($tabLink.length) { + $tabLink.first().trigger('click'); + } + } + }; + + /** + * Given a room id, remove the tab and all child elements from the interface. + * + * @param {string} roomId + */ + FirechatUI.prototype.removeTab = function(roomId) { + delete this.$messages[roomId]; + + // Remove the inner tab content. + this.$tabContent.find('[data-room-id=' + roomId + ']').remove(); + + // Remove the tab from the navigation menu. + this.$tabList.find('[data-room-id=' + roomId + ']').remove(); + + // Dynamically update the width of each tab based upon the number open. + var tabs = this.$tabList.children('li'); + var tabWidth = Math.floor($('#firechat-tab-list').width() / tabs.length); + this.$tabList.children('li').css('width', tabWidth); + + // Automatically select the next tab if there is one. + this.$tabList.find('[data-toggle=tab]').first().trigger('click'); + + // Update the room listing to reflect that we're now in the room. + this.$roomList.children('[data-room-id=' + roomId + ']').children('a').removeClass('highlight'); + }; + + /** + * Render a new message in the specified chat room. + * + * @param {string} roomId + * @param {string} message + */ + FirechatUI.prototype.showMessage = function(roomId, rawMessage) { + var self = this; + + // Setup defaults + var message = { + id : rawMessage.id, + localtime : self.formatTime(rawMessage.timestamp), + message : rawMessage.message || '', + userId : rawMessage.userId, + name : rawMessage.name, + type : rawMessage.type || 'default', + isSelfMessage : (self._user && rawMessage.userId == self._user.id), + disableActions : (!self._user || rawMessage.userId == self._user.id) + }; + + // While other data is escaped in the Underscore.js templates, escape and + // process the message content here to add additional functionality (add links). + // Also trim the message length to some client-defined maximum. + var messageConstructed = ''; + message.message = _.map(message.message.split(' '), function(token) { + if (self.urlPattern.test(token) || self.pseudoUrlPattern.test(token)) { + return self.linkify(encodeURI(token)); + } else { + return _.escape(token); + } + }).join(' '); + message.message = self.trimWithEllipsis(message.message, self.maxLengthMessage); + + // Populate and render the message template. + var template = FirechatDefaultTemplates["templates/message.html"]; + var $message = $(template(message)); + var $messages = self.$messages[roomId]; + if ($messages) { + + var scrollToBottom = false; + if ($messages.scrollTop() / ($messages[0].scrollHeight - $messages[0].offsetHeight) >= 0.95) { + // Pinned to bottom + scrollToBottom = true; + } else if ($messages[0].scrollHeight <= $messages.height()) { + // Haven't added the scrollbar yet + scrollToBottom = true; + } + + $messages.append($message); + + if (scrollToBottom) { + $messages.scrollTop($messages[0].scrollHeight); + } + } + }; + + /** + * Remove a message by id. + * + * @param {string} roomId + * @param {string} messageId + */ + FirechatUI.prototype.removeMessage = function(roomId, messageId) { + $('.message[data-message-id="' + messageId + '"]').remove(); + }; + + /** + * Given a selector for a list element, sort the items alphabetically. + * + * @param {string} selector + */ + FirechatUI.prototype.sortListLexicographically = function(selector) { + $(selector).children("li").sort(function(a, b) { + var upA = $(a).text().toUpperCase(); + var upB = $(b).text().toUpperCase(); + return (upA < upB) ? -1 : (upA > upB) ? 1 : 0; + }).appendTo(selector); + }; + + /** + * Remove leading and trailing whitespace from a string and shrink it, with + * added ellipsis, if it exceeds a specified length. + * + * @param {string} str + * @param {number} length + * @return {string} + */ + FirechatUI.prototype.trimWithEllipsis = function(str, length) { + str = str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + return (length && str.length <= length) ? str : str.substring(0, length) + '...'; + }; + + /** + * Given a timestamp, format it in the form hh:mm am/pm. Defaults to now + * if the timestamp is undefined. + * + * @param {Number} timestamp + * @param {string} date + */ + FirechatUI.prototype.formatTime = function(timestamp) { + var date = (timestamp) ? new Date(timestamp) : new Date(), + hours = date.getHours() || 12, + minutes = '' + date.getMinutes(), + ampm = (date.getHours() >= 12) ? 'pm' : 'am'; + + hours = (hours > 12) ? hours - 12 : hours; + minutes = (minutes.length < 2) ? '0' + minutes : minutes; + return '' + hours + ':' + minutes + ampm; + }; + + /** + * Launch a prompt to allow the user to create a new room. + */ + FirechatUI.prototype.promptCreateRoom = function() { + var self = this; + var template = FirechatDefaultTemplates["templates/prompt-create-room.html"]; + + var $prompt = this.prompt('Create Public Room', template({ + maxLengthRoomName: this.maxLengthRoomName, + isModerator: self._chat.userIsModerator() + })); + $prompt.find('a.close').first().click(function() { + $prompt.remove(); + }); + + + $prompt.find('[data-toggle=submit]').first().click(function() { + var name = $prompt.find('[data-input=firechat-room-name]').first().val(); + if (name !== '') { + self._chat.createRoom(name, 'public'); + $prompt.remove(); + } + }); + + $prompt.find('[data-input=firechat-room-name]').first().focus(); + $prompt.find('[data-input=firechat-room-name]').first().bind('keydown', function(e) { + if (e.which === 13) { + var name = $prompt.find('[data-input=firechat-room-name]').first().val(); + if (name !== '') { + self._chat.createRoom(name, 'public'); + $prompt.remove(); + return false; + } + } + }); + }; + + /** + * Inner method to launch a prompt given a specific title and HTML content. + * @param {string} title + * @param {string} content + */ + FirechatUI.prototype.prompt = function(title, content) { + var template = FirechatDefaultTemplates["templates/prompt.html"], + $prompt; + + $prompt = $(template({ + title: title, + content: content + })).css({ + top: this.$wrapper.position().top + (0.333 * this.$wrapper.height()), + left: this.$wrapper.position().left + (0.125 * this.$wrapper.width()), + width: 0.75 * this.$wrapper.width() + }); + this.$wrapper.append($prompt.removeClass('hidden')); + return $prompt; + }; + + // see http://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links + FirechatUI.prototype.linkify = function(str) { + var self = this; + return str + .replace(self.urlPattern, '$&') + .replace(self.pseudoUrlPattern, '$1$2'); + }; + +})(jQuery); diff --git a/firechat/0.1.2/firechat-default.min.css b/firechat/0.1.2/firechat-default.min.css new file mode 100644 index 0000000..34b54b6 --- /dev/null +++ b/firechat/0.1.2/firechat-default.min.css @@ -0,0 +1 @@ +@charset "UTF-8";#firechat div,#firechat span,#firechat applet,#firechat object,#firechat iframe,#firechat h1,#firechat h2,#firechat h3,#firechat h4,#firechat h5,#firechat h6,#firechat p,#firechat blockquote,#firechat pre,#firechat a,#firechat abbr,#firechat acronym,#firechat address,#firechat big,#firechat cite,#firechat code,#firechat del,#firechat dfn,#firechat em,#firechat img,#firechat ins,#firechat kbd,#firechat q,#firechat s,#firechat samp,#firechat small,#firechat strike,#firechat strong,#firechat sub,#firechat sup,#firechat tt,#firechat var,#firechat b,#firechat u,#firechat i,#firechat center,#firechat dl,#firechat dt,#firechat dd,#firechat ol,#firechat ul,#firechat li,#firechat fieldset,#firechat form,#firechat label,#firechat legend,#firechat table,#firechat caption,#firechat tbody,#firechat tfoot,#firechat thead,#firechat tr,#firechat th,#firechat td,#firechat article,#firechat aside,#firechat canvas,#firechat details,#firechat embed,#firechat figure,#firechat figcaption,#firechat footer,#firechat header,#firechat hgroup,#firechat menu,#firechat nav,#firechat output,#firechat ruby,#firechat section,#firechat summary,#firechat time,#firechat mark,#firechat audio,#firechat video{border:0;font-size:12px;font-family:arial,helvetica,sans-serif;vertical-align:baseline;margin:0;padding:0}#firechat article,#firechat aside,#firechat details,#firechat figcaption,#firechat figure,#firechat footer,#firechat header,#firechat hgroup,#firechat menu,#firechat nav,#firechat section{display:block}#firechat body{line-height:1}#firechat ol,#firechat ul{list-style:none}#firechat blockquote,#firechat q{quotes:none}#firechat blockquote:before,#firechat blockquote:after,#firechat q:before,#firechat q:after{content:none}#firechat table{border-collapse:collapse;border-spacing:0}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}.clearfix:after{clear:both}#firechat{color:#333;text-align:left}#firechat .center{float:none!important;margin-left:auto!important;margin-right:auto!important}#firechat .left{float:left!important}#firechat .right{float:right!important}#firechat .alignleft{text-align:left!important}#firechat .alignright{text-align:right!important}#firechat .aligncenter{text-align:center!important}#firechat .hidden{display:none!important}#firechat .row{clear:both}#firechat .fifth,#firechat .fivesixth,#firechat .fourfifth,#firechat .half,#firechat .ninetenth,#firechat .quarter,#firechat .sevententh,#firechat .sixth,#firechat .tenth,#firechat .third,#firechat .threefifth,#firechat .threequarter,#firechat .threetenth,#firechat .twofifth,#firechat .twothird,#firechat .full{margin-left:2.127659574468085%;float:left;min-height:1px}#firechat .fifth:first-child,#firechat .fivesixth:first-child,#firechat .fourfifth:first-child,#firechat .half:first-child,#firechat .ninetenth:first-child,#firechat .quarter:first-child,#firechat .sevententh:first-child,#firechat .sixth:first-child,#firechat .tenth:first-child,#firechat .third:first-child,#firechat .threefifth:first-child,#firechat .threequarter:first-child,#firechat .threetenth:first-child,#firechat .twofifth:first-child,#firechat .twothird:first-child,#firechat .full:first-child{margin-left:0}#firechat .tenth{width:8.08510638297872%}#firechat .sixth{width:14.893617021276595%}#firechat .fifth{width:18.297872340425535%}#firechat .quarter{width:23.404255319148938%}#firechat .threetenth{width:26.3829787235%}#firechat .third{width:31.914893617021278%}#firechat .twofifth{width:38.72340425531915%}#firechat .half{width:48.93617021276596%}#firechat .sevententh{width:58.7234042555%}#firechat .threefifth{width:59.14893617021278%}#firechat .twothird{width:65.95744680851064%}#firechat .threequarter{width:74.46808510638297%}#firechat .ninetenth{width:74.8936170215%}#firechat .fourfifth{width:79.57446808510639%}#firechat .fivesixth{width:82.9787234042553%}#firechat .full{width:100%}#firechat .clipped{overflow:hidden}#firechat strong{font-weight:bold}#firechat em{font-style:italic}#firechat label{display:block}#firechat a{color:#005580}#firechat a:visited,#firechat a:hover,#firechat a:active{color:#005580}#firechat p{margin:10px 0}#firechat h1,#firechat h2,#firechat h3,#firechat h4,#firechat h5,#firechat h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit}#firechat h1,#firechat h2,#firechat h3{line-height:40px}#firechat h1{font-size:38.5px}#firechat h2{font-size:31.5px}#firechat h3{font-size:24.5px}#firechat h4{font-size:17.5px}#firechat h5{font-size:14px}#firechat h6{font-size:11.9px}#firechat small{font-size:90%}#firechat .nav{list-style:none}#firechat .nav>li>a{display:block;background-color:#eee;text-decoration:none;overflow:hidden;white-space:nowrap}#firechat .nav>li>a:hover,#firechat .nav>li>a:focus{background-color:#fff}#firechat .nav-tabs{border-bottom:1px solid #ddd;clear:both}#firechat .nav-tabs>li{float:left;margin-bottom:-1px;max-width:45%}#firechat .nav-tabs>li>a{-webkit-border-top-right-radius:4px;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:4px;padding:4px 8px;margin-right:2px;line-height:20px;border:1px solid transparent;border-color:#ccc}#firechat .nav-tabs>.active>a,#firechat .nav-tabs>.active>a:hover,#firechat .nav-tabs>.active>a:focus{border-bottom-color:transparent;background-color:#fff;cursor:default}#firechat .tab-content{overflow:auto}#firechat .tab-content>.tab-pane{display:none}#firechat .tab-content>.active{display:block;background-color:#fff}#firechat .caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";margin-top:8px;margin-left:2px}#firechat .dropdown{position:relative}#firechat .dropdown-toggle{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none}#firechat .dropdown-toggle:focus,#firechat .dropdown-toggle:active{outline:0;text-decoration:none}#firechat .dropdown-toggle.btn{padding:4px 0 0;height:22px}#firechat .dropdown-menu{*zoom:1;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top-left-radius:0;z-index:1000;display:none;float:left;position:absolute;top:100%;left:0;width:100%;background-color:#fff;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;min-width:98%;padding:0;margin:-1px 0 0}#firechat .dropdown-menu:before,#firechat .dropdown-menu:after{display:table;content:"";line-height:0}#firechat .dropdown-menu:after{clear:both}#firechat .dropdown-menu ul{background-color:#fff;list-style:none;overflow-y:scroll;max-height:300px}#firechat .dropdown-menu ul>li>a{display:block;padding:1px 1px 1px 3px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}#firechat .dropdown-menu ul>li>a.highlight{background-color:#d9edf7}#firechat .dropdown-menu ul>li>a:hover,#firechat .dropdown-menu ul>li>a:focus,#firechat .dropdown-menu ul>.active>a,#firechat .dropdown-menu ul>.active>a:hover,#firechat .dropdown-menu ul>.active>a:focus{text-decoration:none;color:#000;background-color:#d9edf7;outline:0}#firechat .dropdown-menu ul>.disabled>a,#firechat .dropdown-menu ul>.disabled>a:hover,#firechat .dropdown-menu ul>.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;background-image:none;cursor:default}#firechat .dropdown-header{position:relative;width:100%;padding:10px 0;background-color:#eee;border-bottom:1px solid #ccc}#firechat .dropdown-footer{position:relative;width:100%;padding:10px 0;background-color:#eee;border-top:1px solid #ccc;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#firechat .open{*z-index:1000}#firechat .open>.dropdown-menu{display:block;border:1px solid #ccc;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top-left-radius:0}#firechat .open>.dropdown-toggle{outline:0;text-decoration:none;-webkit-border-top-right-radius:4px;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:4px}#firechat .prompt-wrapper{position:absolute;z-index:1000}#firechat .prompt{position:absolute;z-index:1001;background-color:#fff;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.45);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.45);box-shadow:0 5px 10px rgba(0,0,0,0.45)}#firechat .prompt-header{padding:4px 8px;font-weight:bold;background-color:#eee;border:1px solid #ccc;-webkit-border-top-right-radius:4px;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:4px}#firechat .prompt-header a.close{opacity:.6;font-size:13px;margin-top:2px}#firechat .prompt-header a.close:hover{opacity:.9}#firechat .prompt-body{background-color:#fff;padding:4px 8px;border-left:1px solid #ccc;border-right:1px solid #ccc}#firechat .prompt-footer{padding:4px 8px;background-color:#eee;border:1px solid #ccc;-webkit-border-top-right-radius:0;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-webkit-border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top-left-radius:0}#firechat .prompt-background{background-color:#333;border:1px solid #333;opacity:.8;z-index:1000;height:100%;width:100%}#firechat .btn{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;height:24px;display:inline-block;*display:inline;*zoom:1;padding:2px 5px;margin-bottom:0;text-align:center;vertical-align:middle;cursor:pointer;color:#333;font-size:12px;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #ccc;*border:0;border-bottom-color:#b3b3b3;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}#firechat .btn:hover,#firechat .btn:focus,#firechat .btn:active,#firechat .btn.active,#firechat .btn.disabled,#firechat .btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9;outline:0}#firechat .btn:active,#firechat .btn.active{background-color:#ccc}#firechat .btn:first-child{*margin-left:0}#firechat .btn:hover,#firechat .btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}#firechat .btn.active,#firechat .btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}#firechat .btn.disabled,#firechat .btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65)}#firechat .btn.disabled:active,#firechat .btn[disabled]:active{-webkit-box-shadow:inherit;-moz-box-shadow:inherit;box-shadow:inherit;background-color:#e6e6e6}#firechat .contextmenu{position:fixed;z-index:1001;min-width:150px;border:1px solid #ccc;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}#firechat .contextmenu ul{background-color:#fff;list-style:none}#firechat .contextmenu ul>li>a{display:block;padding:3px 10px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}#firechat .contextmenu ul>li>a.highlight{background-color:#d9edf7}#firechat .contextmenu ul>li>a:hover,#firechat .contextmenu ul>li>a:focus{text-decoration:none;color:#fff;background-color:#0081c2;outline:0}#firechat{padding:0;font-family:sans-serif;font-size:12px;line-height:18px}#firechat input,#firechat textarea{width:100%;font-family:sans-serif;font-size:12px;line-height:18px;padding:2px 5px;border:1px solid #ccc;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#firechat input:-moz-placeholder,#firechat textarea:-moz-placeholder{color:#aaa}#firechat input:-ms-input-placeholder,#firechat textarea:-ms-input-placeholder{color:#aaa}#firechat input::-webkit-input-placeholder,#firechat textarea::-webkit-input-placeholder{color:#aaa}#firechat input[disabled],#firechat textarea[disabled]{background-color:#eee}#firechat input{height:24px}#firechat textarea{resize:none;height:40px}#firechat .search-wrapper{-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;border:1px solid #ccc;margin:0 5px;padding:2px 5px;background:#fff}#firechat .search-wrapper>input[type=text]{padding-left:0;border:0}#firechat .search-wrapper>input[type=text]:focus,#firechat .search-wrapper>input[type=text]:active{outline:0}#firechat .chat{overflow:auto;-ms-overflow-x:hidden;overflow-x:hidden;height:290px;position:relative;margin-bottom:5px;border:1px solid #ccc;border-top:0;overflow-y:scroll}#firechat .chat textarea{overflow:auto;vertical-align:top}#firechat .message{color:#333;padding:3px 5px;border-bottom:1px solid #ccc}#firechat .message.highlighted{background-color:#d9edf7}#firechat .message .name{font-weight:bold;overflow-x:hidden}#firechat .message.message-self{color:#2675ab}#firechat .message:nth-child(odd){background-color:#f9f9f9}#firechat .message:nth-child(odd).highlighted{background-color:#d9edf7}#firechat .message:nth-child(odd).message-local{background-color:#effafc}#firechat .message-content{word-wrap:break-word;padding-right:45px}#firechat .message-content.red{color:red}#firechat .message.message-notification .message-content{font-style:italic}#firechat ul::-webkit-scrollbar{-webkit-appearance:none;width:7px}#firechat ul::-webkit-scrollbar-thumb{border-radius:4px;-webkit-box-shadow:0 0 1px rgba(255,255,255,0.5)}#firechat #firechat-header{padding:6px 0 0 0;height:40px}#firechat #firechat-tabs{height:435px}#firechat #firechat-tab-list{background-color:#fff}#firechat #firechat-tab-content{width:100%;background-color:#fff}#firechat .tab-pane-menu{border:1px solid #ccc;border-top:0;vertical-align:middle;padding-bottom:5px}#firechat .tab-pane-menu .dropdown{margin:5px 0 0 5px}#firechat .tab-pane-menu>.icon{margin:5px 2px 0}#firechat .icon{display:inline-block;*margin-right:.3em;line-height:20px;vertical-align:middle;background-repeat:no-repeat;padding:0;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL8AAAANEAYAAACoeGM7AAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAM80lEQVR42t2cZ4xWRRfHZ3dhwbK7CJZYYiOxV+wlUQzCKmBBsWPvYjQiaiyxYUwULLErltgrNpRF4q5iV0BRLFE0diUi7LIq6Pqw74f//rzO4Zm9d+5z9c37ni//3OfemXtm5sypc58q55xzrrpauHSpcOONheedJ9x9d2Hv3sLvvxfefbfw5puFf/5Zvr+8VFUl7Ozs/rkNNxTef79w222F77wjPOoo4ccfF/O+UDvGzTVEf8wH1zU1wlJJePTRwv33Fx57rPDnn/1+wbzzG+IXvrhmPUPzY/kJEc/HzqslK1fLLy8cMUK4667CFVYQ/vqr8PXXhU884f8eL6eXXKJ2wqSd3tDY2KOHc84NGqSRnnCC7tbVCdvbhRMnasWnTdt5Z+eca2pK6797Yh6ef1641lrCPfYQfv21UNwl62qv115b2Nws/PZb4d57C3/7LftalaOQ/C63nPCII4Tsg6228scHffqp8LHHhPfe6/8eu49feEH44YfCq64S/v67cMGC7tuvu65w6FDhllsKkS/0zoQJaZzMmSO+JaEzZmgkY8b88Ydzzs2b19HhnHNffSWJ6tVLM1cq/fKLrjXiUaPUrn9/XZ9yip6rqVmyxDnnnnrKvPbAA4V6TTJxP/zgT4zdyC+9JOzXz++PgceSVSS1tUJtE+c22si/f+21Pj9Wwd54o//8ZpsJd9xR2KtX9+8vmkLzcsABPt9z5wr32af88xiOf4oaGoSHH17M+/LKg203aJDwiy+EVh5DyHzuuWcxfDn31lvOOffMM7Nn6z1SsZ2dn30m/OQT4eefC7/8UvjBB0LaVzav7D873vfeE6LQISvv3Of50P6OJetgWFp5ZeFzz5V/b1bEEd1333x80g+O4mWXCXEQMDB33CG8/XYhBhuDmMZnOn30kZ5/+23nnGtpefNN55wbP96Xl/nzhYsWCdvauEbeQP1Of6US/XRZfDz8Bx8U9uwpPPFE4eOPC/EMVl9dOG6ccORI4Q03CA87LHbA5RcCwgO47TbhokXCYcOERCKQ7GJiMLiPokeQ2QCjRwtvukkY6zHg0V10kXDAAH++EIz77hM++aRwxRWFq64q3H57v9/+/YVPPy2cOFF47rnCNE8kll/Gi4eIAWU9v/vOnz8MA4qjb18hG51+2JiLF+ebX/hA4U+b5t+/7jrhAw8IkQ88WvnPiZzi4Q0eXL6/dEJhawaGDydmk4uEQmhu1kj79tVImb8RI8TZwIG0p78ddnAubOhDhIcPIXd4nnh4OHYYzPXXF7K/ed5GBLb/NLKRFOtMfyCZAiIKns8aSVp9xP4i8nv//Ti+icjgl0iDeUsjpKDSyJYeqqs1A+3taDBpkn79Qi5YW9vf5wXq0YP+1L5U6loAUjpW4WPhQhN00EFC2SXnDj1UiAF44w2hTWnEEqkcCIWJYkShQNbDIIJZZx2h9Xhs/7EpFDwCNs6UKf584tGQsiFCGTJEuMEG/jxBzBe/H3+8cJddhBiAZ58thl/eM2uW8MgjhWyAyy8XojhRUGuuKayvF7Jhf/pJ+O67wltvFU6dGscv78fTgg45RPjII+XbYZA23bT8/Vtu8ecBDy9MpHS0EROFr1B76lQp7r32su20EwYMkGSOG4dq4I0a4fDh9G9TQN0TKR0iB8aD3G+9tRDHDoPPenKf53GYiABiPWm7f046SUgKDs8aQwxlVfiQTVXh0BChxir+kDol5WPv24im+Ahcb6iu9jVnR4euGX9oXsLj7GKYHP68eUI8ANsRA7WKE8sNkUopdvgJsW0QUBbc3oewl6GQs9LUDgr85JOF5OZHjRKyESZNEp55ppBIC7IbxgoSkQzt2OgXX1wMv9RCFBAuG4HgSREKU0vB42Jjo6gxtPvtJ8TzTM91+kQOf731hHj4KHybA0bRK8B1bpNNhAqkE8OH40CKLZ3I4a+0kq6l8JuapPAbGx991Lm/r5s8+aFDlWOdOVMuS0ODNm5bm1RWSwsSrJY2FZVGRFYYYgwt+wO5EpeJ4eaa+zxPexQ+/Wcl+iFCxeBvs43QRujW048lu09Y16KI/m3Ewu8h/VS55w/5b7AGMrbW5lyXImQh8EhtqGBDGOu525QDnm5RhOcBzZ8vfO01IYIaev/Mmf5zpALsfSiv5bYLfeqpQhSUrYHY92GYbD+shx0XKSOKlrFk1xHFTQRon7M1FNtepaOErwsvFBIqkzKKlQ8MDoTnCpFKwyCi2FZbTYhDgydO6gfaaaesnFC0xQcUUQwlBi6VpPCHDJGKmDyZGWMldY2nPmmSP5NEdlkJRYSCZr7FRbiIzTX3eZ72tiiclZjf444rzyd6hkid9+fNCNhxvfJKvn5C/YVy9nZVYw89ZCdfI6TVEuCLCI4Zhp+Ojq6FIAdLFR0PjpQOFpyFsQMidw5ZxRo7ATaEI6fMgFD8FJsxXCgee00qSiWPJNfKaYLp08u/P1YQ4ZfxWgWDAcOjZmMR8G+3nZBTPbwffhYuFJ5zjhCPKi9ZBUzkh8dpBTkk2KSKLr1UiAIJEYYhK2GQIOto4OGj8NdYQ4hck8tnvkmtQSGDHKK6On8nUNtIPHwUvp05SeSwYTI1s2YpJhk5kliO/uP4Qe6IxMnhk9JhXqyCtQ4Oz9Oe/nwzl05EtJbI5fMeTs8QscIP+zwtEuA+csz+fvjhOH5tf6SSoZCjEipeKw5MVj12PemZ9tXVvunt2dOuHE+K06oqvbG2lp2ikS1ZIknv3burO45lXn+9kKItOfzQwhO6jR0r5PiXPaUQmzNHkMllIyhMMBudmgKnjqxnwnSssooQhWpTLOR6UVwc/4ul1lYhx7rwRBF4PBEEwxLFavhkeTmmN2aMEAG3ghdrqHx149z555efx9DGY37vvFNoPcxQe1v0SyObe8cRIYf/4otCPHwUPsVge4yXWgTEumWl9nZtsPp6irbk8OVyTJ5sYyRJdGOjkitJjUP3FyzwNzIOV1ZivdinFG3J4dt5twqW9eJ5nUFKDIB1xNIIh8p6oijUa64RcnyT2SLSJHWXdtqKcdD/BRcIifBiCf44TIGe2WILYaiGhWHFoaFGxnxtvnlWDvwZY/yLF6toy+8LF2rGamrESXK8WRIwe7YcjKYmzdD06Xpi7lzFWqR4/1pYeywMT5ncL4rpiiuE9tgnIRyeozP9ZyVSBNZyskHxpFFUCGroOCf36Zf28MtzRCqxOXPaM24iJxsJQQgOCgyiqM4GJvduKZRbjOWXXH+fPkKKiUQmOnyYRBrwZeeXCIxxQ/mPSfpEkZn3KZO+7PHiH38U2mKurQHY478ooHSSaZswgWOZum5uFra2iqPOTs0g93EE4NzWAKZM4RioIoDx4/PNE4YQxWNTs6HUgL1Pe3sMNCuF9qOVG2penIPHYOHokcJ8+WVhS4sQR4NaJP3F7luI9tSALBFxovhBW6uonFSORm7mzMEAzJjhnHMNDdLI9fXgq68651xdnZLYffpY+cpIhLzkUNNySd98I7QKFPs0cGC+4dv3sCAYIE4bhXJboWvwrruExxwjJPWStyjD8/b0AyEiCt4Wh6xBpCi+225CG5kUdWogxC/HMyldshGJ7IhcWIfTTxciLxhWqKgcJxuPc/h2nTAAaYrq4IP9dni01jCEiVM3nIdWdaizUxuTDdvaSgQQ6kfPNTVhKPgOgP7j5id0Dh9DbR05Unlch54PfQeQRuyrUM7ZOoxkCq6+WkikhiOBIQ/JEx7sGWfE8QlZOYKyOi7sS+uQgdn3ra/4LT/ZSS1razEEfBgoOf0rdRUaIB4rXyCedpqQFAu5KzxdPyBJPPRlj7d1Tw89JLznHiGeb+hDFVIWIQPll9SS3ynKUUwj5RWbI6Q/inWVEgJsUxRFKdKi+T3rLKEtshf9IZytPdgIgNM75PA5dWQ9fBAFE0+cu+dDGQyAHwGMHi0P/sordT12LB4+Ct//0Cbvh1zWINr9gAePIYe4thGCbU//sYRHn/fDLBwyawhDOfe8ESbvCx3/zHuKJp5Cil8cVlVlxcjXhv5yICvh4aPwreDlJY5nUay1Cj2rINn2lD4qPX6KwcPzRQHhKeP5WeQ+ng2nSyhCcv4fKip1EuIXRQlfXIMUn7nPl9SkhKhFFM2v7QcDEIoAQoiHbxV+5V/uEgGQAuJLXRQ7X/KS0sHD///8cpcIl/P69MN3GDh2RBzUvvjLFVI2NuK1einty+CsZDMY/z0qyuOvkGwIY8+zMuE2ZYGHj8JHUWQlm/sm1ENRZ81dWrShLEVAy1/20F9EEZrTQnk9HJCNYL+YLMrD+F/jF7IbnBQQNQDOi/MFJ9fk8O26xisMQmb7OykaagD+p/RLl3JNDj+U0gn13z0xLhQ0BtEqfLtP7TXP057+YvcDCpvDInwvYInUcnIqqjLKK2/IydlnF8NHfvq3FP9/ANgTCJ9z9ZF7AAAAAElFTkSuQmCC) no-repeat top left;opacity:.3;font-size:22px;font-family:Arial;font-weight:bold;overflow:hidden}#firechat .icon.plus{margin-top:0;vertical-align:top;background:transparent}#firechat .icon.search{background-position:0 0;width:13px;height:13px}#firechat .icon.close{background-position:-120px 0;width:13px;height:13px}#firechat .icon.user-chat{background-position:-138px 0;width:17px;height:13px}#firechat .icon.user-group{background-position:-18px 0;width:17px;height:13px}#firechat .icon.user-mute{background-position:-84px 0;width:13px;height:13px}#firechat .icon.user-mute.red{background-position:-102px 0;width:13px;height:13px}#firechat .icon:hover,#firechat .btn:hover>.icon{opacity:.6}#firechat a>.icon{margin:3px 1px} \ No newline at end of file diff --git a/firechat/0.1.2/firechat-default.min.js b/firechat/0.1.2/firechat-default.min.js new file mode 100644 index 0000000..d7a7ecd --- /dev/null +++ b/firechat/0.1.2/firechat-default.min.js @@ -0,0 +1,2 @@ +(function(){var e=this,t=e._,n={},a=Array.prototype,i=Object.prototype,r=Function.prototype,s=a.push,o=a.slice,c=a.concat,l=i.toString,u=i.hasOwnProperty,d=a.forEach,h=a.map,p=a.reduce,f=a.reduceRight,m=a.filter,v=a.every,_=a.some,g=a.indexOf,b=a.lastIndexOf,y=Array.isArray,w=Object.keys,x=r.bind,R=function(e){return e instanceof R?e:this instanceof R?(this._wrapped=e,void 0):new R(e)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=R),exports._=R):e._=R,R.VERSION="1.4.4";var j=R.each=R.forEach=function(e,t,a){if(null!=e)if(d&&e.forEach===d)e.forEach(t,a);else if(e.length===+e.length){for(var i=0,r=e.length;r>i;i++)if(t.call(a,e[i],i,e)===n)return}else for(var s in e)if(R.has(e,s)&&t.call(a,e[s],s,e)===n)return};R.map=R.collect=function(e,t,n){var a=[];return null==e?a:h&&e.map===h?e.map(t,n):(j(e,function(e,i,r){a[a.length]=t.call(n,e,i,r)}),a)};var I="Reduce of empty array with no initial value";R.reduce=R.foldl=R.inject=function(e,t,n,a){var i=arguments.length>2;if(null==e&&(e=[]),p&&e.reduce===p)return a&&(t=R.bind(t,a)),i?e.reduce(t,n):e.reduce(t);if(j(e,function(e,r,s){i?n=t.call(a,n,e,r,s):(n=e,i=!0)}),!i)throw new TypeError(I);return n},R.reduceRight=R.foldr=function(e,t,n,a){var i=arguments.length>2;if(null==e&&(e=[]),f&&e.reduceRight===f)return a&&(t=R.bind(t,a)),i?e.reduceRight(t,n):e.reduceRight(t);var r=e.length;if(r!==+r){var s=R.keys(e);r=s.length}if(j(e,function(o,c,l){c=s?s[--r]:--r,i?n=t.call(a,n,e[c],c,l):(n=e[c],i=!0)}),!i)throw new TypeError(I);return n},R.find=R.detect=function(e,t,n){var a;return k(e,function(e,i,r){return t.call(n,e,i,r)?(a=e,!0):void 0}),a},R.filter=R.select=function(e,t,n){var a=[];return null==e?a:m&&e.filter===m?e.filter(t,n):(j(e,function(e,i,r){t.call(n,e,i,r)&&(a[a.length]=e)}),a)},R.reject=function(e,t,n){return R.filter(e,function(e,a,i){return!t.call(n,e,a,i)},n)},R.every=R.all=function(e,t,a){t||(t=R.identity);var i=!0;return null==e?i:v&&e.every===v?e.every(t,a):(j(e,function(e,r,s){return(i=i&&t.call(a,e,r,s))?void 0:n}),!!i)};var k=R.some=R.any=function(e,t,a){t||(t=R.identity);var i=!1;return null==e?i:_&&e.some===_?e.some(t,a):(j(e,function(e,r,s){return i||(i=t.call(a,e,r,s))?n:void 0}),!!i)};R.contains=R.include=function(e,t){return null==e?!1:g&&e.indexOf===g?-1!=e.indexOf(t):k(e,function(e){return e===t})},R.invoke=function(e,t){var n=o.call(arguments,2),a=R.isFunction(t);return R.map(e,function(e){return(a?t:e[t]).apply(e,n)})},R.pluck=function(e,t){return R.map(e,function(e){return e[t]})},R.where=function(e,t,n){return R.isEmpty(t)?n?null:[]:R[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},R.findWhere=function(e,t){return R.where(e,t,!0)},R.max=function(e,t,n){if(!t&&R.isArray(e)&&e[0]===+e[0]&&65535>e.length)return Math.max.apply(Math,e);if(!t&&R.isEmpty(e))return-1/0;var a={computed:-1/0,value:-1/0};return j(e,function(e,i,r){var s=t?t.call(n,e,i,r):e;s>=a.computed&&(a={value:e,computed:s})}),a.value},R.min=function(e,t,n){if(!t&&R.isArray(e)&&e[0]===+e[0]&&65535>e.length)return Math.min.apply(Math,e);if(!t&&R.isEmpty(e))return 1/0;var a={computed:1/0,value:1/0};return j(e,function(e,i,r){var s=t?t.call(n,e,i,r):e;a.computed>s&&(a={value:e,computed:s})}),a.value},R.shuffle=function(e){var t,n=0,a=[];return j(e,function(e){t=R.random(n++),a[n-1]=a[t],a[t]=e}),a};var T=function(e){return R.isFunction(e)?e:function(t){return t[e]}};R.sortBy=function(e,t,n){var a=T(t);return R.pluck(R.map(e,function(e,t,i){return{value:e,index:t,criteria:a.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,a=t.criteria;if(n!==a){if(n>a||void 0===n)return 1;if(a>n||void 0===a)return-1}return e.indexr;){var o=r+s>>>1;i>n.call(a,e[o])?r=o+1:s=o}return r},R.toArray=function(e){return e?R.isArray(e)?o.call(e):e.length===+e.length?R.map(e,R.identity):R.values(e):[]},R.size=function(e){return null==e?0:e.length===+e.length?e.length:R.keys(e).length},R.first=R.head=R.take=function(e,t,n){return null==e?void 0:null==t||n?e[0]:o.call(e,0,t)},R.initial=function(e,t,n){return o.call(e,0,e.length-(null==t||n?1:t))},R.last=function(e,t,n){return null==e?void 0:null==t||n?e[e.length-1]:o.call(e,Math.max(e.length-t,0))},R.rest=R.tail=R.drop=function(e,t,n){return o.call(e,null==t||n?1:t)},R.compact=function(e){return R.filter(e,R.identity)};var M=function(e,t,n){return j(e,function(e){R.isArray(e)?t?s.apply(n,e):M(e,t,n):n.push(e)}),n};R.flatten=function(e,t){return M(e,t,[])},R.without=function(e){return R.difference(e,o.call(arguments,1))},R.uniq=R.unique=function(e,t,n,a){R.isFunction(t)&&(a=n,n=t,t=!1);var i=n?R.map(e,n,a):e,r=[],s=[];return j(i,function(n,a){(t?a&&s[s.length-1]===n:R.contains(s,n))||(s.push(n),r.push(e[a]))}),r},R.union=function(){return R.uniq(c.apply(a,arguments))},R.intersection=function(e){var t=o.call(arguments,1);return R.filter(R.uniq(e),function(e){return R.every(t,function(t){return R.indexOf(t,e)>=0})})},R.difference=function(e){var t=c.apply(a,o.call(arguments,1));return R.filter(e,function(e){return!R.contains(t,e)})},R.zip=function(){for(var e=o.call(arguments),t=R.max(R.pluck(e,"length")),n=Array(t),a=0;t>a;a++)n[a]=R.pluck(e,""+a);return n},R.object=function(e,t){if(null==e)return{};for(var n={},a=0,i=e.length;i>a;a++)t?n[e[a]]=t[a]:n[e[a][0]]=e[a][1];return n},R.indexOf=function(e,t,n){if(null==e)return-1;var a=0,i=e.length;if(n){if("number"!=typeof n)return a=R.sortedIndex(e,t),e[a]===t?a:-1;a=0>n?Math.max(0,i+n):n}if(g&&e.indexOf===g)return e.indexOf(t,n);for(;i>a;a++)if(e[a]===t)return a;return-1},R.lastIndexOf=function(e,t,n){if(null==e)return-1;var a=null!=n;if(b&&e.lastIndexOf===b)return a?e.lastIndexOf(t,n):e.lastIndexOf(t);for(var i=a?n:e.length;i--;)if(e[i]===t)return i;return-1},R.range=function(e,t,n){1>=arguments.length&&(t=e||0,e=0),n=arguments[2]||1;for(var a=Math.max(Math.ceil((t-e)/n),0),i=0,r=Array(a);a>i;)r[i++]=e,e+=n;return r},R.bind=function(e,t){if(e.bind===x&&x)return x.apply(e,o.call(arguments,1));var n=o.call(arguments,2);return function(){return e.apply(t,n.concat(o.call(arguments)))}},R.partial=function(e){var t=o.call(arguments,1);return function(){return e.apply(this,t.concat(o.call(arguments)))}},R.bindAll=function(e){var t=o.call(arguments,1);return 0===t.length&&(t=R.functions(e)),j(t,function(t){e[t]=R.bind(e[t],e)}),e},R.memoize=function(e,t){var n={};return t||(t=R.identity),function(){var a=t.apply(this,arguments);return R.has(n,a)?n[a]:n[a]=e.apply(this,arguments)}},R.delay=function(e,t){var n=o.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},R.defer=function(e){return R.delay.apply(R,[e,1].concat(o.call(arguments,1)))},R.throttle=function(e,t){var n,a,i,r,s=0,o=function(){s=new Date,i=null,r=e.apply(n,a)};return function(){var c=new Date,l=t-(c-s);return n=this,a=arguments,0>=l?(clearTimeout(i),i=null,s=c,r=e.apply(n,a)):i||(i=setTimeout(o,l)),r}},R.debounce=function(e,t,n){var a,i;return function(){var r=this,s=arguments,o=function(){a=null,n||(i=e.apply(r,s))},c=n&&!a;return clearTimeout(a),a=setTimeout(o,t),c&&(i=e.apply(r,s)),i}},R.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}},R.wrap=function(e,t){return function(){var n=[e];return s.apply(n,arguments),t.apply(this,n)}},R.compose=function(){var e=arguments;return function(){for(var t=arguments,n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},R.after=function(e,t){return 0>=e?t():function(){return 1>--e?t.apply(this,arguments):void 0}},R.keys=w||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)R.has(e,n)&&(t[t.length]=n);return t},R.values=function(e){var t=[];for(var n in e)R.has(e,n)&&t.push(e[n]);return t},R.pairs=function(e){var t=[];for(var n in e)R.has(e,n)&&t.push([n,e[n]]);return t},R.invert=function(e){var t={};for(var n in e)R.has(e,n)&&(t[e[n]]=n);return t},R.functions=R.methods=function(e){var t=[];for(var n in e)R.isFunction(e[n])&&t.push(n);return t.sort()},R.extend=function(e){return j(o.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},R.pick=function(e){var t={},n=c.apply(a,o.call(arguments,1));return j(n,function(n){n in e&&(t[n]=e[n])}),t},R.omit=function(e){var t={},n=c.apply(a,o.call(arguments,1));for(var i in e)R.contains(n,i)||(t[i]=e[i]);return t},R.defaults=function(e){return j(o.call(arguments,1),function(t){if(t)for(var n in t)null==e[n]&&(e[n]=t[n])}),e},R.clone=function(e){return R.isObject(e)?R.isArray(e)?e.slice():R.extend({},e):e},R.tap=function(e,t){return t(e),e};var U=function(e,t,n,a){if(e===t)return 0!==e||1/e==1/t;if(null==e||null==t)return e===t;e instanceof R&&(e=e._wrapped),t instanceof R&&(t=t._wrapped);var i=l.call(e);if(i!=l.call(t))return!1;switch(i){case"[object String]":return e==t+"";case"[object Number]":return e!=+e?t!=+t:0==e?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if("object"!=typeof e||"object"!=typeof t)return!1;for(var r=n.length;r--;)if(n[r]==e)return a[r]==t;n.push(e),a.push(t);var s=0,o=!0;if("[object Array]"==i){if(s=e.length,o=s==t.length)for(;s--&&(o=U(e[s],t[s],n,a)););}else{var c=e.constructor,u=t.constructor;if(c!==u&&!(R.isFunction(c)&&c instanceof c&&R.isFunction(u)&&u instanceof u))return!1;for(var d in e)if(R.has(e,d)&&(s++,!(o=R.has(t,d)&&U(e[d],t[d],n,a))))break;if(o){for(d in t)if(R.has(t,d)&&!s--)break;o=!s}}return n.pop(),a.pop(),o};R.isEqual=function(e,t){return U(e,t,[],[])},R.isEmpty=function(e){if(null==e)return!0;if(R.isArray(e)||R.isString(e))return 0===e.length;for(var t in e)if(R.has(e,t))return!1;return!0},R.isElement=function(e){return!(!e||1!==e.nodeType)},R.isArray=y||function(e){return"[object Array]"==l.call(e)},R.isObject=function(e){return e===Object(e)},j(["Arguments","Function","String","Number","Date","RegExp"],function(e){R["is"+e]=function(t){return l.call(t)=="[object "+e+"]"}}),R.isArguments(arguments)||(R.isArguments=function(e){return!(!e||!R.has(e,"callee"))}),true&&(R.isFunction=function(e){return"function"==typeof e}),R.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},R.isNaN=function(e){return R.isNumber(e)&&e!=+e},R.isBoolean=function(e){return e===!0||e===!1||"[object Boolean]"==l.call(e)},R.isNull=function(e){return null===e},R.isUndefined=function(e){return void 0===e},R.has=function(e,t){return u.call(e,t)},R.noConflict=function(){return e._=t,this},R.identity=function(e){return e},R.times=function(e,t,n){for(var a=Array(e),i=0;e>i;i++)a[i]=t.call(n,i);return a},R.random=function(e,t){return null==t&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var C={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};C.unescape=R.invert(C.escape);var E={escape:RegExp("["+R.keys(C.escape).join("")+"]","g"),unescape:RegExp("("+R.keys(C.unescape).join("|")+")","g")};R.each(["escape","unescape"],function(e){R[e]=function(t){return null==t?"":(""+t).replace(E[e],function(t){return C[e][t]})}}),R.result=function(e,t){if(null==e)return null;var n=e[t];return R.isFunction(n)?n.call(e):n},R.mixin=function(e){j(R.functions(e),function(t){var n=R[t]=e[t];R.prototype[t]=function(){var e=[this._wrapped];return s.apply(e,arguments),$.call(this,n.apply(R,e))}})};var A=0;R.uniqueId=function(e){var t=++A+"";return e?e+t:t},R.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var D=/(.)^/,L={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},N=/\\|'|\r|\n|\t|\u2028|\u2029/g;R.template=function(e,t,n){var a;n=R.defaults({},n,R.templateSettings);var i=RegExp([(n.escape||D).source,(n.interpolate||D).source,(n.evaluate||D).source].join("|")+"|$","g"),r=0,s="__p+='";e.replace(i,function(t,n,a,i,o){return s+=e.slice(r,o).replace(N,function(e){return"\\"+L[e]}),n&&(s+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),a&&(s+="'+\n((__t=("+a+"))==null?'':__t)+\n'"),i&&(s+="';\n"+i+"\n__p+='"),r=o+t.length,t}),s+="';\n",n.variable||(s="with(obj||{}){\n"+s+"}\n"),s="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+s+"return __p;\n";try{a=Function(n.variable||"obj","_",s)}catch(o){throw o.source=s,o}if(t)return a(t,R);var c=function(e){return a.call(this,e,R)};return c.source="function("+(n.variable||"obj")+"){\n"+s+"}",c},R.chain=function(e){return R(e).chain()};var $=function(e){return this._chain?R(e).chain():e};R.mixin(R),j(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=a[e];R.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!=e&&"splice"!=e||0!==n.length||delete n[0],$.call(this,n)}}),j(["concat","join","slice"],function(e){var t=a[e];R.prototype[e]=function(){return $.call(this,t.apply(this._wrapped,arguments))}}),R.extend(R.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),this.FirechatDefaultTemplates=this.FirechatDefaultTemplates||{},this.FirechatDefaultTemplates["templates/layout-full.html"]=function(obj){obj||(obj={});var __p="";with(_.escape,obj)__p+="
                          \n
                          \n
                          \n\n
                          \n
                          \n
                          \n\n
                          \n
                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/layout-popout.html"]=function(obj){obj||(obj={});var __p="";with(_.escape,obj)__p+="
                          \n
                          \n\n
                          \n
                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/message-context-menu.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                          \n\n
                          ";return __p},this.FirechatDefaultTemplates["templates/message.html"]=function(obj){obj||(obj={});var __t,__p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                          \n
                          \n",disableActions||(__p+="\n\n"),__p+="
                          \n
                          \n"+(null==(__t=message)?"":__t)+"\n
                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt-alert.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                          \n
                          "+__e(message)+"
                          \n

                          \n\n

                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt-create-room.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                          \n
                          Give your chat room a name:
                          \n\n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt-invitation.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                          \n
                          "+__e(fromUserName)+"
                          \n

                          invited you to join

                          \n
                          "+__e(toRoomName)+"
                          \n

                          \n\n\n

                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt-invite-private.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                          \n
                          Invite "+__e(userName)+" to "+__e(roomName)+"?
                          \n

                          \n\n\n

                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt-invite-reply.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                          \n
                          "+__e(toUserName)+"
                          \n

                          \n",__p+="accepted"===status?" accepted your invite. ":" declined your invite. ",__p+="\n

                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt-user-mute.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                          \n
                          "+__e(userName)+"
                          \n

                          \n\n\n

                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/prompt.html"]=function(obj){obj||(obj={});var __t,__p="",__e=_.escape;with(obj)__p+="";return __p},this.FirechatDefaultTemplates["templates/room-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                        • \n\n"+__e(name)+"\n\n
                        • ";return __p},this.FirechatDefaultTemplates["templates/room-user-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                        • \n\n"+__e(name)+"",disableActions||(__p+="\n \n \n"),__p+="\n\n
                        • ";return __p},this.FirechatDefaultTemplates["templates/room-user-search-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                        • \n\n",__p+=disableActions?"\n"+__e(name)+"\n":"\n"+__e(name)+"\n+\n",__p+="\n\n
                        • ";return __p},this.FirechatDefaultTemplates["templates/tab-content.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                          \n
                          \n
                          \n
                          \n\n\n
                          \n
                          ";return __p},this.FirechatDefaultTemplates["templates/tab-menu-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(obj)__p+="
                        • \n"+__e(name)+"\n
                        • ";return __p},this.FirechatDefaultTemplates["templates/user-search-list-item.html"]=function(obj){obj||(obj={});var __p="",__e=_.escape;with(Array.prototype.join,obj)__p+="
                        • \n\n",__p+=disableActions?"\n"+__e(name)+"\n":"\n"+__e(name)+"\n \n",__p+="\n\n
                        • ";return __p},function(){Function.prototype.bind||(Function.prototype.bind=function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),n=this,a=function(){},i=function(){return n.apply(this instanceof a&&e?this:e,t.concat(Array.prototype.slice.call(arguments)))};return a.prototype=this.prototype,i.prototype=new a,i}),Object.keys=Object.keys||function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t}}(),function(e){function t(e,t){this._firebase=e,this._user=null,this._userId=null,this._userName=null,this._isModerator=!1,this._sessionId=null,this._events={},this._rooms={},this._presenceBits={},this._userRef=null,this._messageRef=this._firebase.child("room-messages"),this._roomRef=this._firebase.child("room-metadata"),this._privateRoomRef=this._firebase.child("room-private-metadata"),this._moderatorsRef=this._firebase.child("moderators"),this._suspensionsRef=this._firebase.child("suspensions"),this._usersOnlineRef=this._firebase.child("user-names-online"),this._options=t||{},this._options.numMaxMessages=this._options.numMaxMessages||50}var n=this,a=n.Firechat;t.noConflict=function(){return n.Firechat=a,t},n.Firechat=t,t.prototype={_loadUserMetadata:function(e){var t=this;this._userRef.transaction(function(e){return e&&e.id&&e.name?void 0:{id:t._userId,name:t._userName}},function(a,i,r){t._user=r.val(),t._moderatorsRef.child(t._userId).once("value",function(a){t._isModerator=!!a.val(),n.setTimeout(e,0)})})},_setupDataEvents:function(){this._firebase.root().child(".info/connected").on("value",function(e){if(e.val()===!0)for(var t=0;this._presenceBits>t;t++){var n=this._presenceBits[t],a=this._firebase.root().child(n.ref);a.onDisconnect().set(n.offlineValue),a.set(n.onlineValue)}},this);var e=this._userRef.child("sessions").push();this._sessionId=e.name(),this._queuePresenceOperation(e,!0,null);var t=this._usersOnlineRef.child(this._userName.toLowerCase()),n=t.child(this._sessionId);this._queuePresenceOperation(n,{id:this._userId,name:this._userName},null),this._userRef.on("value",this._onUpdateUser,this),this._userRef.child("invites").on("child_added",this._onFirechatInvite,this),this._userRef.child("notifications").on("child_added",this._onNotification,this)},_addEventCallback:function(e,t){this._events[e]=this._events[e]||[],this._events[e].push(t)},_getEventCallbacks:function(e){return this._events.hasOwnProperty(e)?this._events[e]:[]},_invokeEventCallbacks:function(e){var t=[],n=this._getEventCallbacks(e);Array.prototype.push.apply(t,arguments),t=t.slice(1);for(var a=0;n.length>a;a+=1)n[a].apply(null,t)},_queuePresenceOperation:function(e,t,n){e.onDisconnect().set(n),e.set(t),this._presenceBits[""+e]={ref:e,onlineValue:t,offlineValue:n}},_removePresenceOperation:function(t,n){var a=new e(t);a.onDisconnect().cancel(),a.set(n),delete this._presenceBits[t]},_onUpdateUser:function(e){this._user=e.val(),this._invokeEventCallbacks("user-update",this._user)},_onAuthRequired:function(){this._invokeEventCallbacks("auth-required")},_onEnterRoom:function(e){this._invokeEventCallbacks("room-enter",e)},_onNewMessage:function(e,t){var n=t.val();n.id=t.name(),this._invokeEventCallbacks("message-add",e,n)},_onRemoveMessage:function(e,t){var n=t.name();this._invokeEventCallbacks("message-remove",e,n)},_onLeaveRoom:function(e){this._invokeEventCallbacks("room-exit",e)},_onNotification:function(t){var n=t.val();n.read||(("suspension"!==n.notificationType||n.data.suspendedUntil2&&(r=arguments[1]),a=r?a.limit(r):a,a.once("value",function(e){var t=e.val()||{},a={};for(var r in t)for(var s in t[r]){a[r]=t[r][s];break}n.setTimeout(function(){i(a)},0)})},t.prototype.getUsersByPrefix=function(e,t,a,i,r){var s=this._usersOnlineRef,o=e.toLowerCase();s=t?s.startAt(null,t):a?s.endAt(null,a):o?s.startAt(null,o):s.startAt(),s=i?s.limit(i):s,s.once("value",function(t){var a=t.val()||{},i={};for(var s in a){var c,l,u=a[s];for(var d in u){c=u[d].name,l=u[d].id;break}e.length>0&&0!==c.toLowerCase().indexOf(o)||(i[c]={name:c,id:l})}n.setTimeout(function(){r(i)},0)})},t.prototype.getRoom=function(e,t){this._roomRef.child(e).once("value",function(e){t(e.val())})},t.prototype.userIsModerator=function(){return this._isModerator}}(Firebase),function(e){function t(t,n,a){if(!t)throw Error("FirechatUI: Missing required argument `firebaseRef`");if(!n)throw Error("FirechatUI: Missing required argument `el`");a=a||{},this._options=a,this._el=n,this._user=null,this._chat=new Firechat(t,a),this._roomQueue=[],this.maxLengthUsername=15,this.maxLengthUsernameDisplay=15,this.maxLengthRoomName=24,this.maxLengthMessage=120,this.maxUserSearchResults=100,this.urlPattern=/\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim,this.pseudoUrlPattern=/(^|[^\/])(www\.[\S]+(\b|$))/gim,this._renderLayout(),this.$wrapper=e("#firechat"),this.$roomList=e("#firechat-room-list"),this.$tabList=e("#firechat-tab-list"),this.$tabContent=e("#firechat-tab-content"),this.$messages={},this.$rateLimit={limitCount:10,limitInterval:1e4,limitWaitTime:3e4,history:{}},this._bindUIEvents(),this._bindDataEvents()}if(!e||170>parseInt(e().jquery.replace(/\./g,""),10))throw Error("jQuery 1.7 or later required!");var n=this,a=n.FirechatUI;if(n.FirechatUI=t,!self.FirechatDefaultTemplates)throw Error("Unable to find chat templates!");t.noConflict=function(){return n.FirechatUI=a,t},t.prototype={_bindUIEvents:function(){this._bindForHeightChange(),this._bindForTabControls(),this._bindForRoomList(),this._bindForUserRoomList(),this._bindForUserSearch(),this._bindForUserMuting(),this._bindForChatInvites(),this._bindForRoomListing(),this._setupTabs(),this._setupDropdowns(),this._bindTextInputFieldLimits()},_bindDataEvents:function(){this._chat.on("user-update",this._onUpdateUser.bind(this)),this._chat.on("room-enter",this._onEnterRoom.bind(this)),this._chat.on("room-exit",this._onLeaveRoom.bind(this)),this._chat.on("message-add",this._onNewMessage.bind(this)),this._chat.on("message-remove",this._onRemoveMessage.bind(this)),this._chat.on("room-invite",this._onChatInvite.bind(this)),this._chat.on("room-invite-response",this._onChatInviteResponse.bind(this)),this._chat.on("notification",this._onNotification.bind(this))},_renderLayout:function(){var t=FirechatDefaultTemplates["templates/layout-full.html"];e(this._el).html(t({maxLengthUsername:this.maxLengthUsername}))},_onUpdateUser:function(t){this._user=t;var n=this._user.muted||{};e('[data-event="firechat-user-mute-toggle"]').each(function(){var t=e(this).closest("[data-user-id]").data("user-id");e(this).toggleClass("red",!!n[t])});for(var a in n)e('.message[data-user-id="'+a+'"]').fadeOut()},_onEnterRoom:function(e){this.attachTab(e.id,e.name)},_onLeaveRoom:function(e){this.removeTab(e),this._roomQueue.length>0&&this._chat.enterRoom(this._roomQueue.shift(e))},_onNewMessage:function(e,t){var n=t.userId;this._user&&this._user.muted&&this._user.muted[n]||this.showMessage(e,t)},_onRemoveMessage:function(e,t){this.removeMessage(e,t)},_onChatInvite:function(e){var t=this,n=FirechatDefaultTemplates["templates/prompt-invitation.html"],a=this.prompt("Invite",n(e));a.find("a.close").click(function(){a.remove(),t._chat.declineInvite(e.id)}),a.find("[data-toggle=accept]").click(function(){a.remove(),t._chat.acceptInvite(e.id)}),a.find("[data-toggle=decline]").click(function(){a.remove(),t._chat.declineInvite(e.id)})},_onChatInviteResponse:function(e){if(e.status){var t,n=this,a=FirechatDefaultTemplates["templates/prompt-invite-reply.html"];e.status&&"accepted"===e.status?(t=this.prompt("Accepted",a(e)),this._chat.getRoom(e.toRoomId,function(t){n.attachTab(e.toRoomId,t.name)})):t=this.prompt("Declined",a(e)),t.find("a.close").click(function(){t.remove()})}},_onNotification:function(e){if("warning"===e.notificationType)this.renderAlertPrompt("Warning","You are being warned for inappropriate messaging. Further violation may result in temporary or permanent ban of service.");else if("suspension"===e.notificationType){var t=e.data.suspendedUntil,n=Math.round((t-(new Date).getTime())/1e3),a="";if(n>0){if(n>7200){var i=Math.floor(n/3600);a=i+" hours, ",n-=3600*i}a+=Math.floor(n/60)+" minutes",this.renderAlertPrompt("Suspended","A moderator has suspended you for violating site rules. You cannot send messages for another "+a+".")}}}},t.prototype.setUser=function(e,t){var n=this;n._chat.setUser(e,t,function(e){n._user=e,n._chat.userIsModerator()&&n._bindSuperuserUIEvents(),n._chat.resumeSession()})},t.prototype.on=function(e,t){this._chat.on(e,t)},t.prototype._bindSuperuserUIEvents=function(){var t=this,n=function(){var t=e(this),n=t.closest("[data-message-id]").data("message-id"),a=e('[data-message-id="'+n+'"]').closest("[data-user-id]").data("user-id"),i=e('[data-message-id="'+n+'"]').closest("[data-room-id]").data("room-id");return{messageId:n,userId:a,roomId:i}},a=function(){e('[data-toggle="firechat-contextmenu"]').each(function(){e(this).remove()}),e("#firechat .message.highlighted").each(function(){e(this).removeClass("highlighted")})},i=function(i){var r,s=e(this),o=s.closest("[data-message-id]"),c=FirechatDefaultTemplates["templates/message-context-menu.html"],l=n.call(this,i);i.preventDefault(),a(),s.addClass("highlighted"),t._chat.getRoom(l.roomId,function(){r=e(c({id:o.data("message-id")})),r.css({left:i.clientX,top:i.clientY}).appendTo(t.$wrapper)})};e(document).bind("click",{self:this},function(e){e.button&&2==e.button||a()}),e(document).delegate('[data-class="firechat-message"]',"contextmenu",i),e(document).delegate('[data-event="firechat-user-warn"]',"click",function(e){var a=n.call(this,e);t._chat.warnUser(a.userId)}),e(document).delegate('[data-event="firechat-user-suspend-hour"]',"click",function(e){var a=n.call(this,e);t._chat.suspendUser(a.userId,3600)}),e(document).delegate('[data-event="firechat-user-suspend-day"]',"click",function(e){var a=n.call(this,e);t._chat.suspendUser(a.userId,86400)}),e(document).delegate('[data-event="firechat-message-delete"]',"click",function(e){var a=n.call(this,e);t._chat.deleteMessage(a.roomId,a.messageId)})},t.prototype._bindForHeightChange=function(){var t=e(this._el),n=null;setInterval(function(){var a=t.height();a!=n&&(n=a,e(".chat").each(function(){}))},500)},t.prototype._bindForTabControls=function(){var t=this;e(document).delegate('[data-event="firechat-close-tab"]',"click",function(){var n=e(this).closest("[data-room-id]").data("room-id");t._chat.leaveRoom(n)})},t.prototype._bindForRoomList=function(){var t=this;e("#firechat-btn-rooms").bind("click",function(){if(!e(this).parent().hasClass("open")){var n=(e(this),FirechatDefaultTemplates["templates/room-list-item.html"]),a=function(){var n=e(this).parent(),a=n.data("room-id"),i=n.data("room-name");t.$messages[a]?t.focusTab(a):t._chat.enterRoom(a,i)};t._chat.getRoomList(function(i){t.$roomList.empty();for(var r in i){var s=i[r];s.isRoomOpen=!!t.$messages[s.id];var o=e(n(s));o.children("a").bind("click",a),t.$roomList.append(o.toggle(!0))}})}})},t.prototype._bindForUserRoomList=function(){var t=this;e(document).delegate('[data-event="firechat-user-room-list-btn"]',"click",function(n){n.stopPropagation();var a=e(this),i=a.closest("[data-room-id]").data("room-id"),r=FirechatDefaultTemplates["templates/room-user-list-item.html"],s=a.data("target"),o=e("#"+s);o.empty(),t._chat.getUsersByRoom(i,function(n){for(var a in n)user=n[a],user.disableActions=!t._user||user.id===t._user.id,user.nameTrimmed=t.trimWithEllipsis(user.name,t.maxLengthUsernameDisplay),user.isMuted=t._user&&t._user.muted&&t._user.muted[user.id],o.append(e(r(user)));t.sortListLexicographically("#"+s)})})},t.prototype._bindForUserSearch=function(){var t=this,n=function(t){var n=e(this),i=n.data("target"),r=n.data("controls"),s=n.data("template"),o=n.val()||n.data("prefix")||"",c=n.data("startAt")||null,l=n.data("endAt")||null;t.preventDefault(),a(i,s,r,o,c,l)},a=function(n,a,i,r,s,o){var c=e("#"+n),l=e("#"+i),u=FirechatDefaultTemplates[a];t._chat.getUsersByPrefix(r,s,o,t.maxUserSearchResults,function(e){var n,a,i,s,o,d=0;c.empty();for(i in e){var h=e[i];if(h.disableActions=!t._user||h.id===t._user.id,d+=1,c.append(u(h)),1===d)s=h.name.toLowerCase();else if(d>=t.maxUserSearchResults){o=h.name.toLowerCase();break}}l&&(n=l.find('[data-toggle="firechat-pagination-prev"]'),a=l.find('[data-toggle="firechat-pagination-next"]'),o?a.data("event","firechat-user-search").data("startAt",o).data("prefix",r).removeClass("disabled").removeAttr("disabled"):a.data("event",null).data("startAt",null).data("prefix",null).addClass("disabled").attr("disabled","disabled"))})};e(document).delegate('[data-event="firechat-user-search"]',"keyup",n),e(document).delegate('[data-event="firechat-user-search"]',"click",n),e(document).delegate('[data-event="firechat-user-search-btn"]',"click",function(t){t.stopPropagation();var n=e(this).next("div.dropdown-menu").find("input");n.focus(),n.trigger(jQuery.Event("keyup"))}),e(document).delegate('[data-event="firechat-user-search"]',"click",function(e){e.stopPropagation()})},t.prototype._bindForUserMuting=function(){var t=this;e(document).delegate('[data-event="firechat-user-mute-toggle"]',"click",function(n){var a=e(this),i=a.closest("[data-user-id]").data("user-id"),r=a.closest("[data-user-name]").data("user-name"),s=a.hasClass("red"),o=FirechatDefaultTemplates["templates/prompt-user-mute.html"];if(n.preventDefault(),s)t._chat.toggleUserMute(i);else{var c=t.prompt("Mute User?",o({userName:r}));c.find("a.close").first().click(function(){c.remove()}),c.find("[data-toggle=decline]").first().click(function(){c.remove()}),c.find("[data-toggle=accept]").first().click(function(){t._chat.toggleUserMute(i),c.remove()})}})},t.prototype._bindForChatInvites=function(){var t=this,n=function(){var n,a=e(this),i=a.closest("[data-user-id]").data("user-id"),r=a.closest("[data-room-id]").data("room-id"),s=a.closest("[data-user-name]").data("user-name"),o=FirechatDefaultTemplates["templates/prompt-invite-private.html"];t._chat.getRoom(r,function(e){n=t.prompt("Invite",o({userName:s,roomName:e.name})),n.find("a.close").click(function(){n.remove()}),n.find("[data-toggle=decline]").click(function(){n.remove()}),n.find("[data-toggle=accept]").first().click(function(){n.remove(),t._chat.inviteUser(i,r,e.name)})})},a=function(){var n,a=e(this),i=a.closest("[data-user-id]").data("user-id"),r=a.closest("[data-user-name]").data("user-name"),s=FirechatDefaultTemplates["templates/prompt-invite-private.html"];i&&r&&(n=t.prompt("Private Invite",s({userName:r,roomName:"Private Chat"})),n.find("a.close").click(function(){n.remove()}),n.find("[data-toggle=decline]").click(function(){n.remove()}),n.find("[data-toggle=accept]").first().click(function(){n.remove();var e="Private Chat";t._chat.createRoom(e,"private",function(n){t._chat.inviteUser(i,n,e)})}))};e(document).delegate('[data-event="firechat-user-chat"]',"click",a),e(document).delegate('[data-event="firechat-user-invite"]',"click",n)},t.prototype._bindForRoomListing=function(){var t=this,n=e("#firechat-btn-create-room-prompt"),a=e("#firechat-btn-create-room");n.bind("click",function(){t.promptCreateRoom()}),a.bind("click",function(){var n=e("#firechat-input-room-name").val();e("#firechat-prompt-create-room").remove(),t._chat.createRoom(n)})},t.prototype._setupTabs=function(){var t=function(t){var a,i,r=t,s=r.closest("ul:not(.dropdown-menu)"),o=r.attr("data-target"),c=s.find(".active:last a")[0];o||(o=r.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),r.parent("li").hasClass("active")||(i=e.Event("show",{relatedTarget:c}),r.trigger(i),i.isDefaultPrevented()||(a=e(o),n(r.parent("li"),s),n(a,a.parent(),function(){r.trigger({type:"shown",relatedTarget:c})})))},n=function(t,n,a){function i(){r.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?t.addClass("in"):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),a&&a()}var r=n.find("> .active"),s=a&&e.support.transition&&r.hasClass("fade");s?r.one(e.support.transition.end,i):i(),r.removeClass("in")};e(document).delegate('[data-toggle="tab"]',"click",function(n){n.preventDefault(),t(e(this))})},t.prototype._setupDropdowns=function(){var t=function(){var t=e(this),i=a(t),r=i.hasClass("open");if(!t.is(".disabled, :disabled"))return n(),r||i.toggleClass("open"),t.focus(),!1},n=function(){e("[data-toggle=dropdown]").each(function(){a(e(this)).removeClass("open")})},a=function(t){var n,a=t.attr("data-target");return a||(a=t.attr("href"),a=a&&/#/.test(a)&&a.replace(/.*(?=#[^\s]*$)/,"")),n=a&&e(a),n&&n.length||(n=t.parent()),n};e(document).bind("click",n).delegate(".dropdown-menu","click",function(e){e.stopPropagation()}).delegate("[data-toggle=dropdown]","click",t)},t.prototype._bindTextInputFieldLimits=function(){e("body").delegate('input[data-provide="limit"], textarea[data-provide="limit"]',"keyup",function(){var t=e(this),n=e(t.data("counter")),a=t.attr("maxlength"),i=t.val().length;n.html(Math.max(0,a-i))})},t.prototype.renderAlertPrompt=function(e,t){var n=FirechatDefaultTemplates["templates/prompt-alert.html"],a=this.prompt(e,n({message:t}));a.find(".close").click(function(){a.remove()})},t.prototype.toggleInputs=function(t){e("#firechat-tab-content textarea").each(function(){var n=e(this);t?e(this).val(""):e(this).val("You have exceeded the message limit, please wait before sending."),n.prop("disabled",!t)}),e("#firechat-input-name").prop("disabled",!t)},t.prototype.attachTab=function(t,n){var a=this;if(this.$messages[t])return this.focusTab(t),void 0;var i={id:t,name:n},r=FirechatDefaultTemplates["templates/tab-content.html"],s=e(r(i));this.$tabContent.prepend(s);var o=e("#firechat-messages"+t);this.$messages[t]=o;var c=s.find("textarea").first();c.bind("keydown",function(e){var n=a.trimWithEllipsis(c.val(),a.maxLengthMessage);return 13===e.which&&""!==n?(c.val(""),a._chat.sendMessage(t,n),!1):void 0});var l=FirechatDefaultTemplates["templates/tab-menu-item.html"],u=e(l(i));this.$tabList.prepend(u),u.bind("shown",function(){o.scrollTop(o[0].scrollHeight)});var d=this.$tabList.children("li"),h=Math.floor(e("#firechat-tab-list").width()/d.length);this.$tabList.children("li").css("width",h),this.$roomList.children("[data-room-id="+t+"]").children("a").addClass("highlight"),e("#firechat-btn-room-user-list-"+t).bind("click",function(){a.sortListLexicographically("#firechat-room-user-list-"+t)}),this.focusTab(t)},t.prototype.focusTab=function(e){if(this.$messages[e]){var t=this.$tabList.find("[data-room-id="+e+"]").find("a");t.length&&t.first().trigger("click")}},t.prototype.removeTab=function(t){delete this.$messages[t],this.$tabContent.find("[data-room-id="+t+"]").remove(),this.$tabList.find("[data-room-id="+t+"]").remove();var n=this.$tabList.children("li"),a=Math.floor(e("#firechat-tab-list").width()/n.length);this.$tabList.children("li").css("width",a),this.$tabList.find("[data-toggle=tab]").first().trigger("click"),this.$roomList.children("[data-room-id="+t+"]").children("a").removeClass("highlight")},t.prototype.showMessage=function(t,n){var a=this,i={id:n.id,localtime:a.formatTime(n.timestamp),message:n.message||"",userId:n.userId,name:n.name,type:n.type||"default",isSelfMessage:a._user&&n.userId==a._user.id,disableActions:!a._user||n.userId==a._user.id};i.message=_.map(i.message.split(" "),function(e){return a.urlPattern.test(e)||a.pseudoUrlPattern.test(e)?a.linkify(encodeURI(e)):_.escape(e)}).join(" "),i.message=a.trimWithEllipsis(i.message,a.maxLengthMessage);var r=FirechatDefaultTemplates["templates/message.html"],s=e(r(i)),o=a.$messages[t];if(o){var c=!1;o.scrollTop()/(o[0].scrollHeight-o[0].offsetHeight)>=.95?c=!0:o[0].scrollHeight<=o.height()&&(c=!0),o.append(s),c&&o.scrollTop(o[0].scrollHeight)}},t.prototype.removeMessage=function(t,n){e('.message[data-message-id="'+n+'"]').remove()},t.prototype.sortListLexicographically=function(t){e(t).children("li").sort(function(t,n){var a=e(t).text().toUpperCase(),i=e(n).text().toUpperCase();return i>a?-1:a>i?1:0}).appendTo(t)},t.prototype.trimWithEllipsis=function(e,t){return e=e.replace(/^\s\s*/,"").replace(/\s\s*$/,""),t&&t>=e.length?e:e.substring(0,t)+"..."},t.prototype.formatTime=function(e){var t=e?new Date(e):new Date,n=t.getHours()||12,a=""+t.getMinutes(),i=t.getHours()>=12?"pm":"am";return n=n>12?n-12:n,a=2>a.length?"0"+a:a,""+n+":"+a+i},t.prototype.promptCreateRoom=function(){var e=this,t=FirechatDefaultTemplates["templates/prompt-create-room.html"],n=this.prompt("Create Public Room",t({maxLengthRoomName:this.maxLengthRoomName,isModerator:e._chat.userIsModerator()}));n.find("a.close").first().click(function(){n.remove()}),n.find("[data-toggle=submit]").first().click(function(){var t=n.find("[data-input=firechat-room-name]").first().val();""!==t&&(e._chat.createRoom(t,"public"),n.remove())}),n.find("[data-input=firechat-room-name]").first().focus(),n.find("[data-input=firechat-room-name]").first().bind("keydown",function(t){if(13===t.which){var a=n.find("[data-input=firechat-room-name]").first().val();if(""!==a)return e._chat.createRoom(a,"public"),n.remove(),!1}})},t.prototype.prompt=function(t,n){var a,i=FirechatDefaultTemplates["templates/prompt.html"];return a=e(i({title:t,content:n})).css({top:this.$wrapper.position().top+.333*this.$wrapper.height(),left:this.$wrapper.position().left+.125*this.$wrapper.width(),width:.75*this.$wrapper.width()}),this.$wrapper.append(a.removeClass("hidden")),a},t.prototype.linkify=function(e){var t=this;return e.replace(t.urlPattern,'$&').replace(t.pseudoUrlPattern,'$1$2')}}(jQuery); \ No newline at end of file diff --git a/images/customer-cbs.png b/images/customer-cbs.png new file mode 100644 index 0000000..3ac3c83 Binary files /dev/null and b/images/customer-cbs.png differ diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 0000000..43697a3 Binary files /dev/null and b/images/favicon.ico differ diff --git a/images/fork-on-github-white.png b/images/fork-on-github-white.png new file mode 100644 index 0000000..65fbf11 Binary files /dev/null and b/images/fork-on-github-white.png differ diff --git a/images/powered-by-firebase-dim.png b/images/powered-by-firebase-dim.png new file mode 100644 index 0000000..57ab523 Binary files /dev/null and b/images/powered-by-firebase-dim.png differ diff --git a/images/powered-by-firebase.png b/images/powered-by-firebase.png new file mode 100644 index 0000000..69263aa Binary files /dev/null and b/images/powered-by-firebase.png differ diff --git a/images/sign-in-with-twitter.png b/images/sign-in-with-twitter.png new file mode 100644 index 0000000..c2822dc Binary files /dev/null and b/images/sign-in-with-twitter.png differ diff --git a/images/top-shadow.png b/images/top-shadow.png new file mode 100644 index 0000000..233c1fc Binary files /dev/null and b/images/top-shadow.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..2319271 --- /dev/null +++ b/index.html @@ -0,0 +1,248 @@ + + + + + + Firechat - open source chat built on Firebase + + + + + + + + + + + + + + + Fork me on GitHub + + +
                          +
                          +
                          +
                          + +
                          Firechat
                          +
                          Open-source real-time chat, built on Firebase
                          +
                          +
                          + +
                          +
                          + +
                          +
                          +
                          +
                          + Logged in as . + Logout +
                          +
                          +
                          +
                          +
                          + + +
                          +
                          +
                          + +
                          +
                          +
                          +
                          +
                          Real-time chat with no server code.
                          +
                          +
                          +
                          +

                          What is Firechat?

                          +

                          + Firechat is an open-source, real-time chat widget built on Firebase. It offers fully secure multi-user, multi-room chat with flexible authentication, moderator features, user presence and search, private messaging, chat invitations, and more. +

                          +
                          +
                          +

                          Which technologies does Firechat use?

                          +

                          + The core data layer under Firechat uses Firebase for real-time data synchronization and persistence. +

                          + The default interface uses jQuery, Underscore.js, and Bootstrap. Icons by Glyphicons. Build and compilation managed with Grunt and code hosted by GitHub. +

                          +
                          +
                          +
                          +
                          +

                          What can I do with Firechat?

                          +

                          + With Firechat, you get full-featured chat in your application with a few simple script includes. Additionally, Firechat is easy to modify and extend. Based upon it's simple underlying data model and Firebase-powered data synchronization, it's easy to add new features, modify the UI, and customize to fit your specific needs. +

                          +

                          + If Firechat doesn't currently meet your needs, feel free to fork the repo and tweak the code! +

                          +
                          +
                          +

                          Who's behind Firechat?

                          +

                          + Firechat was built by the folks at Firebase in San Francisco, California. +

                          + Community submissions are encouraged! Star Firechat on GitHub and send a pull request when you're ready to contribute! +

                          +
                          +
                          +
                          +
                          +

                          Who's using Firechat?

                          +

                          +

                          + + CBS Big Brother Live Feeds +
                          +

                          +
                          +
                          +

                          How is Firechat Licensed?

                          +

                          + Firechat is published under the MIT license. +

                          +
                          +
                          +
                          +
                          +
                          +
                          + + + + + + + + + + + + + + + + + + + + +
                          + + + \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index 454b491..0000000 --- a/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "firechat", - "title": "Firechat", - "description": "Open source, real-time chat powered by Firebase", - "homepage": "http://firebase.github.io/firechat", - "author": { - "name": "Firebase Inc.", - "url": "https://firebase.com" - }, - "repository": { - "type": "git", - "url": "git://github.com/firebase/firechat.git" - }, - "bugs": "https://github.com/firebase/firechat/issues", - "licenses": [{ - "type": "MIT", - "url": "http://firebase.mit-license.org/" - }], - "version": "0.1.0", - "dependencies": {}, - "devDependencies": { - "grunt": "~0.4.0", - "grunt-contrib-clean": "~0.4.1", - "grunt-contrib-concat": "~0.1.1", - "grunt-contrib-jshint": "~0.1.0", - "grunt-contrib-jst": "~0.5.0", - "grunt-contrib-less": "~0.5.0", - "grunt-contrib-uglify": "~0.1.0", - "grunt-contrib-watch": "~0.1.4", - "grunt-docco": "~0.2.0" - }, - "keywords": ["firebase", "chat", "talk", "real-time", "synchronization", "websocket"] -} \ No newline at end of file diff --git a/src/js/firechat-ui.js b/src/js/firechat-ui.js deleted file mode 100644 index e213993..0000000 --- a/src/js/firechat-ui.js +++ /dev/null @@ -1,1111 +0,0 @@ -(function($) { - - - if (!$ || (parseInt($().jquery.replace(/\./g, ""), 10) < 170)) { - throw new Error("jQuery 1.7 or later required!"); - } - - var root = this, - previousFirechatUI = root.FirechatUI; - - root.FirechatUI = FirechatUI; - - if (!self.FirechatDefaultTemplates) { - throw new Error("Unable to find chat templates!"); - } - - function FirechatUI(firebaseRef, el, options) { - var self = this; - - if (!firebaseRef) { - throw new Error('FirechatUI: Missing required argument `firebaseRef`'); - } - - if (!el) { - throw new Error('FirechatUI: Missing required argument `el`'); - } - - options = options || {}; - this._options = options; - - this._el = el; - this._user = null; - this._chat = new Firechat(firebaseRef, options); - - // A list of rooms to enter once we've made room for them (once we've hit the max room limit). - this._roomQueue = []; - - // Define some constants regarding maximum lengths, client-enforced. - this.maxLengthUsername = 15; - this.maxLengthUsernameDisplay = 15; - this.maxLengthRoomName = 24; - this.maxLengthMessage = 120; - this.maxUserSearchResults = 100; - - // Define some useful regexes. - this.urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim; - this.pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim; - - this._renderLayout(); - - // Grab shortcuts to commonly used jQuery elements. - this.$wrapper = $('#firechat'); - this.$roomList = $('#firechat-room-list'); - this.$tabList = $('#firechat-tab-list'); - this.$tabContent = $('#firechat-tab-content'); - this.$messages = {}; - - // Rate limit messages from a given user with some defaults. - this.$rateLimit = { - limitCount: 10, // max number of events - limitInterval: 10000, // max interval for above count in milliseconds - limitWaitTime: 30000, // wait time if a user hits the wait limit - history: {} - }; - - // Setup UI bindings for chat controls. - this._bindUIEvents(); - - // Setup bindings to internal methods - this._bindDataEvents(); - } - - // Run FirechatUI in *noConflict* mode, returning the `FirechatUI` variable to - // its previous owner, and returning a reference to the FirechatUI object. - FirechatUI.noConflict = function noConflict() { - root.FirechatUI = previousFirechatUI; - return FirechatUI; - }; - - FirechatUI.prototype = { - - _bindUIEvents: function() { - // Chat-specific custom interactions and functionality. - this._bindForHeightChange(); - this._bindForTabControls(); - this._bindForRoomList(); - this._bindForUserRoomList(); - this._bindForUserSearch(); - this._bindForUserMuting(); - this._bindForChatInvites(); - this._bindForRoomListing(); - - // Generic, non-chat-specific interactive elements. - this._setupTabs(); - this._setupDropdowns(); - this._bindTextInputFieldLimits(); - }, - - _bindDataEvents: function() { - this._chat.on('user-update', this._onUpdateUser.bind(this)); - - // Bind events for new messages, enter / leaving rooms, and user metadata. - this._chat.on('room-enter', this._onEnterRoom.bind(this)); - this._chat.on('room-exit', this._onLeaveRoom.bind(this)); - this._chat.on('message-add', this._onNewMessage.bind(this)); - this._chat.on('message-remove', this._onRemoveMessage.bind(this)); - - // Bind events related to chat invitations. - this._chat.on('room-invite', this._onChatInvite.bind(this)); - this._chat.on('room-invite-response', this._onChatInviteResponse.bind(this)); - - // Binds events related to admin or moderator notifications. - this._chat.on('notification', this._onNotification.bind(this)); - }, - - _renderLayout: function() { - var template = FirechatDefaultTemplates["templates/layout-full.html"]; - $(this._el).html(template({ - maxLengthUsername: this.maxLengthUsername - })); - }, - - _onUpdateUser: function(user) { - // Update our current user state and render latest user name. - this._user = user; - - // Update our interface to reflect which users are muted or not. - var mutedUsers = this._user.muted || {}; - $('[data-event="firechat-user-mute-toggle"]').each(function(i, el) { - var userId = $(this).closest('[data-user-id]').data('user-id'); - $(this).toggleClass('red', !!mutedUsers[userId]); - }); - - // Ensure that all messages from muted users are removed. - for (var userId in mutedUsers) { - $('.message[data-user-id="' + userId + '"]').fadeOut(); - } - }, - _onEnterRoom: function(room) { - this.attachTab(room.id, room.name); - }, - _onLeaveRoom: function(roomId) { - this.removeTab(roomId); - - // Auto-enter rooms in the queue - if ((this._roomQueue.length > 0)) { - this._chat.enterRoom(this._roomQueue.shift(roomId)); - } - }, - _onNewMessage: function(roomId, message) { - var userId = message.userId; - if (!this._user.muted || !this._user.muted[userId]) { - this.showMessage(roomId, message); - } - }, - _onRemoveMessage: function(roomId, messageId) { - this.removeMessage(roomId, messageId); - }, - - // Events related to chat invitations. - _onChatInvite: function(invitation) { - var self = this; - var template = FirechatDefaultTemplates["templates/prompt-invitation.html"]; - var $prompt = this.prompt('Invite', template(invitation)); - $prompt.find('a.close').click(function() { - $prompt.remove(); - self._chat.declineInvite(invitation.id); - }); - - $prompt.find('[data-toggle=accept]').click(function() { - $prompt.remove(); - self._chat.acceptInvite(invitation.id); - }); - - $prompt.find('[data-toggle=decline]').click(function() { - $prompt.remove(); - self._chat.declineInvite(invitation.id); - }); - }, - _onChatInviteResponse: function(invitation) { - if (!invitation.status) return; - - var self = this, - template = FirechatDefaultTemplates["templates/prompt-invite-reply.html"], - $prompt; - - if (invitation.status && invitation.status === 'accepted') { - $prompt = this.prompt('Accepted', template(invitation)); - this._chat.getRoom(invitation.toRoomId, function(room) { - self.attachTab(invitation.toRoomId, room.name); - }); - } else { - $prompt = this.prompt('Declined', template(invitation)); - } - - $prompt.find('a.close').click(function() { - $prompt.remove(); - }); - }, - - // Events related to admin or moderator notifications. - _onNotification: function(notification) { - if (notification.notificationType === 'warning') { - this.renderAlertPrompt('Warning', 'You are being warned for inappropriate messaging. Further violation may result in temporary or permanent ban of service.'); - } else if (notification.notificationType === 'suspension') { - var suspendedUntil = notification.data.suspendedUntil, - secondsLeft = Math.round((suspendedUntil - new Date().getTime()) / 1000), - timeLeft = ''; - - if (secondsLeft > 0) { - if (secondsLeft > 2*3600) { - var hours = Math.floor(secondsLeft / 3600); - timeLeft = hours + ' hours, '; - secondsLeft -= 3600*hours; - } - timeLeft += Math.floor(secondsLeft / 60) + ' minutes'; - this.renderAlertPrompt('Suspended', 'A moderator has suspended you for violating site rules. You cannot send messages for another ' + timeLeft + '.'); - } - } - } - }; - - FirechatUI.prototype.initWithUser = function(userId, userName) { - var self = this; - - // Initialize data events - self._chat.initWithUser(userId, userName, function(user) { - self._user = user; - - if (self._chat.userIsModerator()) { - self._bindSuperuserUIEvents(); - } - - self._chat.resumeSession(); - }); - }; - - /** - * Binds a custom context menu to messages for superusers to warn or ban - * users for violating terms of service. - */ - FirechatUI.prototype._bindSuperuserUIEvents = function() { - var self = this, - parseMessageVars = function(event) { - var $this = $(this), - messageId = $this.closest('[data-message-id]').data('message-id'), - userId = $('[data-message-id="' + messageId + '"]').closest('[data-user-id]').data('user-id'), - roomId = $('[data-message-id="' + messageId + '"]').closest('[data-room-id]').data('room-id'); - - return { messageId: messageId, userId: userId, roomId: roomId }; - }, - clearMessageContextMenus = function() { - // Remove any context menus currently showing. - $('[data-toggle="firechat-contextmenu"]').each(function() { - $(this).remove(); - }); - - // Remove any messages currently highlighted. - $('#firechat .message.highlighted').each(function() { - $(this).removeClass('highlighted'); - }); - }, - showMessageContextMenu = function(event) { - var $this = $(this), - $message = $this.closest('[data-message-id]'), - template = FirechatDefaultTemplates["templates/message-context-menu.html"], - messageVars = parseMessageVars.call(this, event), - $template; - - event.preventDefault(); - - // Clear existing menus. - clearMessageContextMenus(); - - // Highlight the relevant message. - $this.addClass('highlighted'); - - self._chat.getRoom(messageVars.roomId, function(room) { - // Show the context menu. - $template = $(template({ - id: $message.data('message-id') - })); - $template.css({ - left: event.clientX, - top: event.clientY - }).appendTo(self.$wrapper); - }); - }; - - // Handle dismissal of message context menus (any non-right-click click event). - $(document).bind('click', { self: this }, function(event) { - if (!event.button || event.button != 2) { - clearMessageContextMenus(); - } - }); - - // Handle display of message context menus (via right-click on a message). - $(document).delegate('[data-class="firechat-message"]', 'contextmenu', showMessageContextMenu); - - // Handle click of the 'Warn User' contextmenu item. - $(document).delegate('[data-event="firechat-user-warn"]', 'click', function(event) { - var messageVars = parseMessageVars.call(this, event); - self._chat.warnUser(messageVars.userId); - }); - - // Handle click of the 'Suspend User (1 Hour)' contextmenu item. - $(document).delegate('[data-event="firechat-user-suspend-hour"]', 'click', function(event) { - var messageVars = parseMessageVars.call(this, event); - self._chat.suspendUser(messageVars.userId, /* 1 Hour = 3600s */ 60*60); - }); - - // Handle click of the 'Suspend User (1 Day)' contextmenu item. - $(document).delegate('[data-event="firechat-user-suspend-day"]', 'click', function(event) { - var messageVars = parseMessageVars.call(this, event); - self._chat.suspendUser(messageVars.userId, /* 1 Day = 86400s */ 24*60*60); - }); - - // Handle click of the 'Delete Message' contextmenu item. - $(document).delegate('[data-event="firechat-message-delete"]', 'click', function(event) { - var messageVars = parseMessageVars.call(this, event); - self._chat.deleteMessage(messageVars.roomId, messageVars.messageId); - }); - }; - - /** - * Binds to height changes in the surrounding div. - */ - FirechatUI.prototype._bindForHeightChange = function() { - var self = this, - $el = $(this._el), - lastHeight = null; - - setInterval(function() { - var height = $el.height(); - if (height != lastHeight) { - lastHeight = height; - $('.chat').each(function(i, el) { - - }); - } - }, 500); - }; - - /** - * Binds custom inner-tab events. - */ - FirechatUI.prototype._bindForTabControls = function() { - var self = this; - - // Handle click of tab close button. - $(document).delegate('[data-event="firechat-close-tab"]', 'click', function(event) { - var roomId = $(this).closest('[data-room-id]').data('room-id'); - self._chat.leaveRoom(roomId); - }); - }; - - /** - * Binds room list dropdown to populate room list on-demand. - */ - FirechatUI.prototype._bindForRoomList = function() { - var self = this; - - $('#firechat-btn-rooms').bind('click', function() { - if ($(this).parent().hasClass('open')) { - return; - } - - var $this = $(this), - template = FirechatDefaultTemplates["templates/room-list-item.html"], - selectRoomListItem = function() { - var parent = $(this).parent(), - roomId = parent.data('room-id'), - roomName = parent.data('room-name'); - - if (self.$messages[roomId]) { - self.focusTab(roomId); - } else { - self._chat.enterRoom(roomId, roomName); - } - }; - - self._chat.getRoomList(function(rooms) { - self.$roomList.empty(); - for (var roomId in rooms) { - var room = rooms[roomId]; - room.isRoomOpen = !!self.$messages[room.id]; - var $roomItem = $(template(room)); - $roomItem.children('a').bind('click', selectRoomListItem); - self.$roomList.append($roomItem.toggle(true)); - } - }); - }); - }; - - /** - * Binds user list dropdown per room to populate user list on-demand. - */ - FirechatUI.prototype._bindForUserRoomList = function() { - var self = this; - - // Upon click of the dropdown, autofocus the input field and trigger list population. - $(document).delegate('[data-event="firechat-user-room-list-btn"]', 'click', function(event) { - event.stopPropagation(); - - var $this = $(this), - roomId = $this.closest('[data-room-id]').data('room-id'), - template = FirechatDefaultTemplates["templates/room-user-list-item.html"], - targetId = $this.data('target'), - $target = $('#' + targetId); - - $target.empty(); - self._chat.getUsersByRoom(roomId, function(users) { - for (var username in users) { - user = users[username]; - user.disableActions = (user.id === self._user.id); - user.nameTrimmed = self.trimWithEllipsis(user.name, self.maxLengthUsernameDisplay); - user.isMuted = (self._user.muted && self._user.muted[user.id]); - $target.append($(template(user))); - } - self.sortListLexicographically('#' + targetId); - }); - }); - }; - - /** - * Binds user search buttons, dropdowns, and input fields for searching all - * active users currently in chat. - */ - FirechatUI.prototype._bindForUserSearch = function() { - var self = this, - handleUserSearchSubmit = function(event) { - var $this = $(this), - targetId = $this.data('target'), - controlsId = $this.data('controls'), - templateId = $this.data('template'), - prefix = $this.val() || $this.data('prefix') || '', - startAt = $this.data('startAt') || null, - endAt = $this.data('endAt') || null; - - event.preventDefault(); - - userSearch(targetId, templateId, controlsId, prefix, startAt, endAt); - }, - userSearch = function(targetId, templateId, controlsId, prefix, startAt, endAt) { - var $target = $('#' + targetId), - $controls = $('#' + controlsId), - template = FirechatDefaultTemplates[templateId]; - - // Query results, filtered by prefix, using the defined startAt and endAt markets. - self._chat.getUsersByPrefix(prefix, startAt, endAt, self.maxUserSearchResults, function(users) { - var numResults = 0, - $prevBtn, $nextBtn, username, firstResult, lastResult; - - $target.empty(); - - for (username in users) { - var user = users[username]; - - // Disable buttons for . - user.disableActions = (user.id === self._user.id); - - numResults += 1; - - $target.append(template(user)); - - // If we've hit our result limit, the additional value signifies we should paginate. - if (numResults === 1) { - firstResult = user.name.toLowerCase(); - } else if (numResults >= self.maxUserSearchResults) { - lastResult = user.name.toLowerCase(); - break; - } - } - - if ($controls) { - $prevBtn = $controls.find('[data-toggle="firechat-pagination-prev"]'); - $nextBtn = $controls.find('[data-toggle="firechat-pagination-next"]'); - - // Sort out configuration for the 'next' button - if (lastResult) { - $nextBtn - .data('event', 'firechat-user-search') - .data('startAt', lastResult) - .data('prefix', prefix) - .removeClass('disabled').removeAttr('disabled'); - } else { - $nextBtn - .data('event', null) - .data('startAt', null) - .data('prefix', null) - .addClass('disabled').attr('disabled', 'disabled'); - } - } - }); - }; - - $(document).delegate('[data-event="firechat-user-search"]', 'keyup', handleUserSearchSubmit); - $(document).delegate('[data-event="firechat-user-search"]', 'click', handleUserSearchSubmit); - - // Upon click of the dropdown, autofocus the input field and trigger list population. - $(document).delegate('[data-event="firechat-user-search-btn"]', 'click', function(event) { - event.stopPropagation(); - var $input = $(this).next('div.dropdown-menu').find('input'); - $input.focus(); - $input.trigger(jQuery.Event('keyup')); - }); - - // Ensure that the dropdown stays open despite clicking on the input element. - $(document).delegate('[data-event="firechat-user-search"]', 'click', function(event) { - event.stopPropagation(); - }); - }; - - /** - * Binds user mute toggles and removes all messages for a given user upon mute. - */ - FirechatUI.prototype._bindForUserMuting = function() { - var self = this; - $(document).delegate('[data-event="firechat-user-mute-toggle"]', 'click', function(event) { - var $this = $(this), - userId = $this.closest('[data-user-id]').data('user-id'), - userName = $this.closest('[data-user-name]').data('user-name'), - isMuted = $this.hasClass('red'), - template = FirechatDefaultTemplates["templates/prompt-user-mute.html"]; - - event.preventDefault(); - - // Require user confirmation for muting. - if (!isMuted) { - var $prompt = self.prompt('Mute User?', template({ - userName: userName - })); - - $prompt.find('a.close').first().click(function() { - $prompt.remove(); - }); - - $prompt.find('[data-toggle=decline]').first().click(function() { - $prompt.remove(); - }); - - $prompt.find('[data-toggle=accept]').first().click(function() { - self._chat.toggleUserMute(userId); - $prompt.remove(); - }); - } else { - self._chat.toggleUserMute(userId); - } - }); - }; - - /** - * Binds to elements with the data-event='firechat-user-(private)-invite' and - * handles invitations as well as room creation and entering. - */ - FirechatUI.prototype._bindForChatInvites = function() { - var self = this, - renderInvitePrompt = function(event) { - var $this = $(this), - userId = $this.closest('[data-user-id]').data('user-id'), - roomId = $this.closest('[data-room-id]').data('room-id'), - userName = $this.closest('[data-user-name]').data('user-name'), - template = FirechatDefaultTemplates["templates/prompt-invite-private.html"], - $prompt; - - self._chat.getRoom(roomId, function(room) { - $prompt = self.prompt('Invite', template({ - userName: userName, - roomName: room.name - })); - - $prompt.find('a.close').click(function() { - $prompt.remove(); - }); - - $prompt.find('[data-toggle=decline]').click(function() { - $prompt.remove(); - }); - - $prompt.find('[data-toggle=accept]').first().click(function() { - $prompt.remove(); - self._chat.inviteUser(userId, roomId, room.name); - }); - }); - }, - renderPrivateInvitePrompt = function(event) { - var $this = $(this), - userId = $this.closest('[data-user-id]').data('user-id'), - userName = $this.closest('[data-user-name]').data('user-name'), - template = FirechatDefaultTemplates["templates/prompt-invite-private.html"], - $prompt; - - if (userId && userName) { - $prompt = self.prompt('Private Invite', template({ - userName: userName, - roomName: 'Private Chat' - })); - - $prompt.find('a.close').click(function() { - $prompt.remove(); - }); - - $prompt.find('[data-toggle=decline]').click(function() { - $prompt.remove(); - }); - - $prompt.find('[data-toggle=accept]').first().click(function() { - $prompt.remove(); - var roomName = 'Private Chat'; - self._chat.createRoom(roomName, 'private', function(roomId) { - self._chat.inviteUser(userId, roomId, roomName); - }); - }); - } - }; - - $(document).delegate('[data-event="firechat-user-chat"]', 'click', renderPrivateInvitePrompt); - $(document).delegate('[data-event="firechat-user-invite"]', 'click', renderInvitePrompt); - }; - - /** - * Binds to room dropdown button, menu items, and create room button. - */ - FirechatUI.prototype._bindForRoomListing = function() { - var self = this, - $createRoomPromptButton = $('#firechat-btn-create-room-prompt'), - $createRoomButton = $('#firechat-btn-create-room'), - renderRoomList = function(event) { - var type = $(this).data('room-type'); - - self.sortListLexicographically('#firechat-room-list'); - }; - - // Handle click of the create new room prompt-button. - $createRoomPromptButton.bind('click', function(event) { - self.promptCreateRoom(); - }); - - // Handle click of the create new room button. - $createRoomButton.bind('click', function(event) { - var roomName = $('#firechat-input-room-name').val(); - $('#firechat-prompt-create-room').remove(); - self._chat.createRoom(roomName); - }); - }; - - /** - * A stripped-down version of bootstrap-tab.js. - * - * Original bootstrap-tab.js Copyright 2012 Twitter, Inc.,licensed under the Apache v2.0 - */ - FirechatUI.prototype._setupTabs = function() { - var self = this, - show = function($el) { - var $this = $el, - $ul = $this.closest('ul:not(.dropdown-menu)'), - selector = $this.attr('data-target'), - previous = $ul.find('.active:last a')[0], - $target, - e; - - if (!selector) { - selector = $this.attr('href'); - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); - } - - if ($this.parent('li').hasClass('active')) return; - - e = $.Event('show', { relatedTarget: previous }); - - $this.trigger(e); - - if (e.isDefaultPrevented()) return; - - $target = $(selector); - - activate($this.parent('li'), $ul); - activate($target, $target.parent(), function () { - $this.trigger({ - type: 'shown', - relatedTarget: previous - }); - }); - }, - activate = function (element, container, callback) { - var $active = container.find('> .active'), - transition = callback && $.support.transition && $active.hasClass('fade'); - - function next() { - $active - .removeClass('active') - .find('> .dropdown-menu > .active') - .removeClass('active'); - - element.addClass('active'); - - if (transition) { - element.addClass('in'); - } else { - element.removeClass('fade'); - } - - if (element.parent('.dropdown-menu')) { - element.closest('li.dropdown').addClass('active'); - } - - if (callback) { - callback(); - } - } - - if (transition) { - $active.one($.support.transition.end, next); - } else { - next(); - } - - $active.removeClass('in'); - }; - - $(document).delegate('[data-toggle="tab"]', 'click', function(event) { - event.preventDefault(); - show($(this)); - }); - }; - - /** - * A stripped-down version of bootstrap-dropdown.js. - * - * Original bootstrap-dropdown.js Copyright 2012 Twitter, Inc., licensed under the Apache v2.0 - */ - FirechatUI.prototype._setupDropdowns = function() { - var self = this, - toggle = '[data-toggle=dropdown]', - toggleDropdown = function(event) { - var $this = $(this), - $parent = getParent($this), - isActive = $parent.hasClass('open'); - - if ($this.is('.disabled, :disabled')) return; - - clearMenus(); - - if (!isActive) { - $parent.toggleClass('open'); - } - - $this.focus(); - - return false; - }, - clearMenus = function() { - $('[data-toggle=dropdown]').each(function() { - getParent($(this)).removeClass('open'); - }); - }, - getParent = function($this) { - var selector = $this.attr('data-target'), - $parent; - - if (!selector) { - selector = $this.attr('href'); - selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); - } - - $parent = selector && $(selector); - - if (!$parent || !$parent.length) $parent = $this.parent(); - - return $parent; - }; - - $(document) - .bind('click', clearMenus) - .delegate('.dropdown-menu', 'click', function(event) { event.stopPropagation(); }) - .delegate('[data-toggle=dropdown]', 'click', toggleDropdown); - }; - - /** - * Binds to any text input fields with data-provide='limit' and - * data-counter='', and upon value change updates the selector - * content to reflect the number of characters remaining, as the 'maxlength' - * attribute less the current value length. - */ - FirechatUI.prototype._bindTextInputFieldLimits = function() { - $('body').delegate('input[data-provide="limit"], textarea[data-provide="limit"]', 'keyup', function(event) { - var $this = $(this), - $target = $($this.data('counter')), - limit = $this.attr('maxlength'), - count = $this.val().length; - - $target.html(Math.max(0, limit - count)); - }); - }; - - /** - * Given a title and message content, show an alert prompt to the user. - * - * @param {string} title - * @param {string} message - */ - FirechatUI.prototype.renderAlertPrompt = function(title, message) { - var template = FirechatDefaultTemplates["templates/prompt-alert.html"], - $prompt = this.prompt(title, template({ message: message })); - - $prompt.find('.close').click(function() { - $prompt.remove(); - }); - return; - }; - - /** - * Toggle input field s if we want limit / unlimit input fields. - */ - FirechatUI.prototype.toggleInputs = function(isEnabled) { - $('#firechat-tab-content textarea').each(function() { - var $this = $(this); - if (isEnabled) { - $(this).val(''); - } else { - $(this).val('You have exceeded the message limit, please wait before sending.'); - } - $this.prop('disabled', !isEnabled); - }); - $('#firechat-input-name').prop('disabled', !isEnabled); - }; - - /** - * Given a room id and name, attach the tab to the interface and setup events. - * - * @param {string} roomId - * @param {string} roomName - */ - FirechatUI.prototype.attachTab = function(roomId, roomName) { - var self = this; - - // If this tab already exists, give it focus. - if (this.$messages[roomId]) { - this.focusTab(roomId); - return; - } - - var room = { - id: roomId, - name: roomName - }; - - // Populate and render the tab content template. - var tabTemplate = FirechatDefaultTemplates["templates/tab-content.html"]; - var $tabContent = $(tabTemplate(room)); - this.$tabContent.prepend($tabContent); - var $messages = $('#firechat-messages' + roomId); - - // Keep a reference to the message listing for later use. - this.$messages[roomId] = $messages; - - // Attach on-enter event to textarea. - var $textarea = $tabContent.find('textarea').first(); - $textarea.bind('keydown', function(e) { - var message = self.trimWithEllipsis($textarea.val(), self.maxLengthMessage); - if ((e.which === 13) && (message !== '')) { - $textarea.val(''); - self._chat.sendMessage(roomId, message); - return false; - } - }); - - // Populate and render the tab menu template. - var tabListTemplate = FirechatDefaultTemplates["templates/tab-menu-item.html"]; - var $tab = $(tabListTemplate(room)); - this.$tabList.prepend($tab); - - // Attach on-shown event to move tab to front and scroll to bottom. - $tab.bind('shown', function(event) { - $messages.scrollTop($messages[0].scrollHeight); - }); - - // Dynamically update the width of each tab based upon the number open. - var tabs = this.$tabList.children('li'); - var tabWidth = Math.floor($('#firechat-tab-list').width() / tabs.length); - this.$tabList.children('li').css('width', tabWidth); - - // Update the room listing to reflect that we're now in the room. - this.$roomList.children('[data-room-id=' + roomId + ']').children('a').addClass('highlight'); - - // Sort each item in the user list alphabetically on click of the dropdown. - $('#firechat-btn-room-user-list-' + roomId).bind('click', function() { - self.sortListLexicographically('#firechat-room-user-list-' + roomId); - }); - - // Automatically select the new tab. - this.focusTab(roomId); - }; - - /** - * Given a room id, focus the given tab. - * - * @param {string} roomId - */ - FirechatUI.prototype.focusTab = function(roomId) { - if (this.$messages[roomId]) { - var $tabLink = this.$tabList.find('[data-room-id=' + roomId + ']').find('a'); - if ($tabLink.length) { - $tabLink.first().trigger('click'); - } - } - }; - - /** - * Given a room id, remove the tab and all child elements from the interface. - * - * @param {string} roomId - */ - FirechatUI.prototype.removeTab = function(roomId) { - delete this.$messages[roomId]; - - // Remove the inner tab content. - this.$tabContent.find('[data-room-id=' + roomId + ']').remove(); - - // Remove the tab from the navigation menu. - this.$tabList.find('[data-room-id=' + roomId + ']').remove(); - - // Dynamically update the width of each tab based upon the number open. - var tabs = this.$tabList.children('li'); - var tabWidth = Math.floor($('#firechat-tab-list').width() / tabs.length); - this.$tabList.children('li').css('width', tabWidth); - - // Automatically select the next tab if there is one. - this.$tabList.find('[data-toggle=tab]').first().trigger('click'); - - // Update the room listing to reflect that we're now in the room. - this.$roomList.children('[data-room-id=' + roomId + ']').children('a').removeClass('highlight'); - }; - - /** - * Render a new message in the specified chat room. - * - * @param {string} roomId - * @param {string} message - */ - FirechatUI.prototype.showMessage = function(roomId, rawMessage) { - var self = this; - - // Setup defaults - var message = { - id : rawMessage.id, - localtime : this.formatTime(rawMessage.timestamp), - message : rawMessage.message || '', - userId : rawMessage.userId, - name : rawMessage.name, - type : rawMessage.type || 'default', - isSelfMessage : (rawMessage.userId == this._user.id) - }; - - // While other data is escaped in the Underscore.js templates, escape and - // process the message content here to add additional functionality (add links). - // Also trim the message length to some client-defined maximum. - var messageConstructed = ''; - message.message = _.map(message.message.split(' '), function(token) { - if (self.urlPattern.test(token) || self.pseudoUrlPattern.test(token)) { - return self.linkify(encodeURI(token)); - } else { - return _.escape(token); - } - }).join(' '); - message.message = this.trimWithEllipsis(message.message, this.maxLengthMessage); - - // Populate and render the message template. - var template = FirechatDefaultTemplates["templates/message.html"]; - var $message = $(template(message)); - var $messages = this.$messages[roomId]; - if ($messages) { - - var scrollToBottom = false; - if ($messages.scrollTop() / ($messages[0].scrollHeight - $messages[0].offsetHeight) >= 0.95) { - // Pinned to bottom - scrollToBottom = true; - } else if ($messages[0].scrollHeight <= $messages.height()) { - // Haven't added the scrollbar yet - scrollToBottom = true; - } - - $messages.append($message); - - if (scrollToBottom) { - $messages.scrollTop($messages[0].scrollHeight); - } - } - }; - - /** - * Remove a message by id. - * - * @param {string} roomId - * @param {string} messageId - */ - FirechatUI.prototype.removeMessage = function(roomId, messageId) { - $('.message[data-message-id="' + messageId + '"]').remove(); - }; - - /** - * Given a selector for a list element, sort the items alphabetically. - * - * @param {string} selector - */ - FirechatUI.prototype.sortListLexicographically = function(selector) { - $(selector).children("li").sort(function(a, b) { - var upA = $(a).text().toUpperCase(); - var upB = $(b).text().toUpperCase(); - return (upA < upB) ? -1 : (upA > upB) ? 1 : 0; - }).appendTo(selector); - }; - - /** - * Remove leading and trailing whitespace from a string and shrink it, with - * added ellipsis, if it exceeds a specified length. - * - * @param {string} str - * @param {number} length - * @return {string} - */ - FirechatUI.prototype.trimWithEllipsis = function(str, length) { - str = str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - return (length && str.length <= length) ? str : str.substring(0, length) + '...'; - }; - - /** - * Given a timestamp, format it in the form hh:mm am/pm. Defaults to now - * if the timestamp is undefined. - * - * @param {Number} timestamp - * @param {string} date - */ - FirechatUI.prototype.formatTime = function(timestamp) { - var date = (timestamp) ? new Date(timestamp) : new Date(), - hours = date.getHours() || 12, - minutes = '' + date.getMinutes(), - ampm = (date.getHours() >= 12) ? 'pm' : 'am'; - - hours = (hours > 12) ? hours - 12 : hours; - minutes = (minutes.length < 2) ? '0' + minutes : minutes; - return '' + hours + ':' + minutes + ampm; - }; - - /** - * Launch a prompt to allow the user to create a new room. - */ - FirechatUI.prototype.promptCreateRoom = function() { - var self = this; - var template = FirechatDefaultTemplates["templates/prompt-create-room.html"]; - - var $prompt = this.prompt('Create Public Room', template({ - maxLengthRoomName: this.maxLengthRoomName, - isModerator: self._chat.userIsModerator() - })); - $prompt.find('a.close').first().click(function() { - $prompt.remove(); - }); - - - $prompt.find('[data-toggle=submit]').first().click(function() { - var name = $prompt.find('[data-input=firechat-room-name]').first().val(); - if (name !== '') { - self._chat.createRoom(name, 'public'); - $prompt.remove(); - } - }); - - $prompt.find('[data-input=firechat-room-name]').first().focus(); - $prompt.find('[data-input=firechat-room-name]').first().bind('keydown', function(e) { - if (e.which === 13) { - var name = $prompt.find('[data-input=firechat-room-name]').first().val(); - if (name !== '') { - self._chat.createRoom(name, 'public'); - $prompt.remove(); - return false; - } - } - }); - }; - - /** - * Inner method to launch a prompt given a specific title and HTML content. - * @param {string} title - * @param {string} content - */ - FirechatUI.prototype.prompt = function(title, content) { - var template = FirechatDefaultTemplates["templates/prompt.html"], - $prompt; - - $prompt = $(template({ - title: title, - content: content - })).css({ - top: this.$wrapper.position().top + (0.333 * this.$wrapper.height()), - left: this.$wrapper.position().left + (0.125 * this.$wrapper.width()), - width: 0.75 * this.$wrapper.width() - }); - this.$wrapper.append($prompt.removeClass('hidden')); - return $prompt; - }; - - // see http://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links - FirechatUI.prototype.linkify = function(str) { - var self = this; - return str - .replace(self.urlPattern, '$&') - .replace(self.pseudoUrlPattern, '$1$2'); - }; - -})(jQuery); diff --git a/src/js/firechat.js b/src/js/firechat.js deleted file mode 100644 index 085aaa0..0000000 --- a/src/js/firechat.js +++ /dev/null @@ -1,603 +0,0 @@ -// Firechat is a simple, easily-extensible data layer for multi-user, -// multi-room chat, built entirely on [Firebase](https://firebase.com). -// -// The Firechat object is the primary conduit for all underlying data events. -// It exposes a number of methods for binding event listeners, creating, -// entering, or leaving chat rooms, initiating chats, sending messages, -// and moderator actions such as warning, kicking, or suspending users. -// -// Firechat.js 0.1.0 -// https://firebase.com -// (c) 2013 Firebase -// License: MIT - -// Setup -// -------------- -(function(Firebase) { - - // Establish a reference to the `window` object, and save the previous value - // of the `Firechat` variable. - var root = this, - previousFirechat = root.Firechat; - - function Firechat(firebaseRef, options) { - - // Instantiate a new connection to Firebase. - this._firebase = firebaseRef; - - // User-specific instance variables. - this._user = null; - this._userId = null; - this._userName = null; - this._isModerator = false; - - // A unique id generated for each session. - this._sessionId = null; - - // A mapping of event IDs to an array of callbacks. - this._events = {}; - - // A mapping of room IDs to a boolean indicating presence. - this._rooms = {}; - - // A mapping of operations to re-queue on disconnect. - this._presenceBits = {}; - - // Commonly-used Firebase references. - this._userRef = null; - this._messageRef = this._firebase.child('room-messages'); - this._roomRef = this._firebase.child('room-metadata'); - this._privateRoomRef = this._firebase.child('room-private-metadata'); - this._moderatorsRef = this._firebase.child('moderators'); - this._suspensionsRef = this._firebase.child('suspensions'); - this._usersOnlineRef = this._firebase.child('user-names-online'); - - // Setup and establish default options. - this._options = options || {}; - - // The number of historical messages to load per room. - this._options.numMaxMessages = this._options.numMaxMessages || 50; - } - - // Run Firechat in *noConflict* mode, returning the `Firechat` variable to - // its previous owner, and returning a reference to the Firechat object. - Firechat.noConflict = function noConflict() { - root.Firechat = previousFirechat; - return Firechat; - }; - - // Export the Firechat object as a global. - root.Firechat = Firechat; - - // Firechat Internal Methods - // -------------- - Firechat.prototype = { - - // Load the initial metadata for the user's account and set initial state. - _loadUserMetadata: function(onComplete) { - var self = this; - - // Update the user record with a default name on user's first visit. - this._userRef.transaction(function(current) { - if (!current || !current.id || !current.name) { - return { - id: self._userId, - name: self._userName - }; - } - }, function(error, committed, snapshot) { - self._user = snapshot.val(); - self._moderatorsRef.child(self._userId).once('value', function(snapshot) { - self._isModerator = !!snapshot.val(); - root.setTimeout(onComplete, 0); - }); - }); - }, - - // Initialize Firebase listeners and callbacks for the supported bindings. - _setupDataEvents: function() { - // Monitor connection state so we can requeue disconnect operations if need be. - this._firebase.root().child('.info/connected').on('value', function(snapshot) { - if (snapshot.val() === true) { - // We're connected (or reconnected)! Set up our presence state. - for (var i = 0; i < this._presenceBits; i++) { - var op = this._presenceBits[i], - ref = this._firebase.root().child(op.ref); - - ref.onDisconnect().set(op.offlineValue); - ref.set(op.onlineValue); - } - } - }, this); - - // Generate a unique session id for the visit. - var sessionRef = this._userRef.child('sessions').push(); - this._sessionId = sessionRef.name(); - this._queuePresenceOperation(sessionRef, true, null); - - // Register our username in the public user listing. - var usernameRef = this._usersOnlineRef.child(this._userName.toLowerCase()); - var usernameSessionRef = usernameRef.child(this._sessionId); - this._queuePresenceOperation(usernameSessionRef, { - id: this._userId, - name: this._userName - }, null); - - // Listen for state changes for the given user. - this._userRef.on('value', this._onUpdateUser, this); - - // Listen for chat invitations from other users. - this._userRef.child('invites').on('child_added', this._onFirechatInvite, this); - - // Listen for messages from moderators and adminstrators. - this._userRef.child('notifications').on('child_added', this._onNotification, this); - }, - - // Append the new callback to our list of event handlers. - _addEventCallback: function(eventId, callback) { - this._events[eventId] = this._events[eventId] || []; - this._events[eventId].push(callback); - }, - - // Retrieve the list of event handlers for a given event id. - _getEventCallbacks: function(eventId) { - if (this._events.hasOwnProperty(eventId)) { - return this._events[eventId]; - } - return []; - }, - - // Invoke each of the event handlers for a given event id with specified data. - _invokeEventCallbacks: function(eventId) { - var args = [], - callbacks = this._getEventCallbacks(eventId); - - Array.prototype.push.apply(args, arguments); - args = args.slice(1); - - for (var i = 0; i < callbacks.length; i += 1) { - callbacks[i].apply(null, args); - } - }, - - // Keep track of on-disconnect events so they can be requeued if we disconnect the reconnect. - _queuePresenceOperation: function(ref, onlineValue, offlineValue) { - ref.onDisconnect().set(offlineValue); - ref.set(onlineValue); - this._presenceBits[ref.toString()] = { - ref: ref, - onlineValue: onlineValue, - offlineValue: offlineValue - }; - }, - - // Remove an on-disconnect event from firing upon future disconnect and reconnect. - _removePresenceOperation: function(path, value) { - var ref = new Firebase(path); - ref.onDisconnect().cancel(); - ref.set(value); - delete this._presenceBits[path]; - }, - - // Event to monitor user current user state. - _onUpdateUser: function(snapshot) { - this._user = snapshot.val(); - this._invokeEventCallbacks('user-update', this._user); - }, - - // Events to monitor room entry / exit and messages additional / removal. - _onEnterRoom: function(room) { - this._invokeEventCallbacks('room-enter', room); - }, - _onNewMessage: function(roomId, snapshot) { - var message = snapshot.val(); - message.id = snapshot.name(); - this._invokeEventCallbacks('message-add', roomId, message); - }, - _onRemoveMessage: function(roomId, snapshot) { - var messageId = snapshot.name(); - this._invokeEventCallbacks('message-remove', roomId, messageId); - }, - _onLeaveRoom: function(roomId) { - this._invokeEventCallbacks('room-exit', roomId); - }, - - // Event to listen for notifications from administrators and moderators. - _onNotification: function(snapshot) { - var notification = snapshot.val(); - if (!notification.read) { - if (notification.notificationType !== 'suspension' || notification.data.suspendedUntil < Firebase.ServerValue.TIMESTAMP) { - snapshot.ref().child('read').set(true); - } - this._invokeEventCallbacks('notification', notification); - } - }, - - // Events to monitor chat invitations and invitation replies. - _onFirechatInvite: function(snapshot) { - var self = this, - invite = snapshot.val(); - - // Skip invites we've already responded to. - if (invite.status) { - return; - } - - invite.id = invite.id || snapshot.name(); - self.getRoom(invite.roomId, function(room) { - invite.toRoomName = room.name; - self._invokeEventCallbacks('room-invite', invite); - }); - }, - _onFirechatInviteResponse: function(snapshot) { - var self = this, - invite = snapshot.val(); - - invite.id = invite.id || snapshot.name(); - this._invokeEventCallbacks('room-invite-response', invite); - } - }; - - // Firechat External Methods - // -------------- - - // Initialize the library and setup data listeners. - Firechat.prototype.initWithUser = function(userId, userName, callback) { - var self = this; - - self._firebase.root().child('.info/authenticated').on('value', function(snapshot) { - if (snapshot.val() === true) { - self._firebase.root().child('.info/authenticated').off(); - - self._userId = userId.toString(); - self._userName = userName.toString(); - self._userRef = self._firebase.child('users').child(self._userId); - self._loadUserMetadata(function() { - root.setTimeout(function() { - callback(self._user); - self._setupDataEvents(); - }, 0); - }); - } - }); - }; - - // Resumes the previous session by automatically entering rooms. - Firechat.prototype.resumeSession = function() { - this._userRef.child('rooms').once('value', function(snapshot) { - var rooms = snapshot.val(); - for (var roomId in rooms) { - this.enterRoom(rooms[roomId].id); - } - }, /* onError */ function(){}, /* context */ this); - }; - - // Callback registration. Supports each of the following events: - Firechat.prototype.on = function(eventType, cb) { - this._addEventCallback(eventType, cb); - }; - - // Create and automatically enter a new chat room. - Firechat.prototype.createRoom = function(roomName, roomType, callback) { - var self = this, - newRoomRef = this._roomRef.push(); - - var newRoom = { - id: newRoomRef.name(), - name: roomName, - type: roomType || 'public', - createdByUserId: this._userId, - createdAt: Firebase.ServerValue.TIMESTAMP - }; - - if (roomType === 'private') { - newRoom.authorizedUsers = {}; - newRoom.authorizedUsers[this._userId] = true; - } - - newRoomRef.set(newRoom, function(error) { - if (!error) { - self.enterRoom(newRoomRef.name()); - } - if (callback) { - callback(newRoomRef.name()); - } - }); - }; - - // Enter a chat room. - Firechat.prototype.enterRoom = function(roomId) { - var self = this; - self.getRoom(roomId, function(room) { - var roomName = room.name; - - if (!roomId || !roomName) return; - - // Skip if we're already in this room. - if (self._rooms[roomId]) { - return; - } - - self._rooms[roomId] = true; - - // Save entering this room to resume the session again later. - self._userRef.child('rooms').child(roomId).set({ - id: roomId, - name: roomName, - active: true - }); - - // Set presence bit for the room and queue it for removal on disconnect. - var presenceRef = self._firebase.child('room-users').child(roomId).child(self._userId).child(self._sessionId); - self._queuePresenceOperation(presenceRef, { - id: self._userId, - name: self._userName - }, null); - - // Invoke our callbacks before we start listening for new messages. - self._onEnterRoom({ id: roomId, name: roomName }); - - // Setup message listeners - self._roomRef.child(roomId).once('value', function(snapshot) { - self._messageRef.child(roomId).limit(self._options.numMaxMessages).on('child_added', function(snapshot) { - self._onNewMessage(roomId, snapshot); - }, /* onCancel */ function() { - // Turns out we don't have permission to access these messages. - self.leaveRoom(roomId); - }, /* context */ self); - - self._messageRef.child(roomId).limit(self._options.numMaxMessages).on('child_removed', function(snapshot) { - self._onRemoveMessage(roomId, snapshot); - }, /* onCancel */ function(){}, /* context */ self); - }, /* onFailure */ function(){}, self); - }); - }; - - // Leave a chat room. - Firechat.prototype.leaveRoom = function(roomId) { - var self = this, - userRoomRef = self._firebase.child('room-users').child(roomId), - presenceRef = userRoomRef.child(self._userId).child(self._sessionId); - - // Remove listener for new messages to this room. - self._messageRef.child(roomId).off(); - - // Remove presence bit for the room and cancel on-disconnect removal. - self._removePresenceOperation(presenceRef.toString(), null); - - // Remove session bit for the room. - self._userRef.child('rooms').child(roomId).remove(); - - delete self._rooms[roomId]; - - // Invoke event callbacks for the room-exit event. - self._onLeaveRoom(roomId); - }; - - Firechat.prototype.sendMessage = function(roomId, messageContent, messageType, cb) { - var self = this, - message = { - userId: self._userId, - name: self._userName, - timestamp: Firebase.ServerValue.TIMESTAMP, - message: messageContent, - type: messageType || 'default' - }, - newMessageRef = self._messageRef.child(roomId).push(); - - newMessageRef.setWithPriority(message, Firebase.ServerValue.TIMESTAMP, cb); - }; - - Firechat.prototype.deleteMessage = function(roomId, messageId, cb) { - var self = this; - - self._messageRef.child(roomId).child(messageId).remove(cb); - }; - - // Mute or unmute a given user by id. This list will be stored internally and - // all messages from the muted clients will be filtered client-side after - // receipt of each new message. - Firechat.prototype.toggleUserMute = function(userId, cb) { - var self = this; - - self._userRef.child('muted').child(userId).transaction(function(isMuted) { - return (isMuted) ? null : true; - }, cb); - }; - - // Send a moderator notification to a specific user. - Firechat.prototype.sendSuperuserNotification = function(userId, notificationType, data, cb) { - var self = this, - userNotificationsRef = self._firebase.child('users').child(userId).child('notifications'); - - userNotificationsRef.push({ - fromUserId: self._userId, - timestamp: Firebase.ServerValue.TIMESTAMP, - notificationType: notificationType, - data: data || {} - }, cb); - }; - - // Warn a user for violating the terms of service or being abusive. - Firechat.prototype.warnUser = function(userId) { - var self = this; - - self.sendSuperuserNotification(userId, 'warning'); - }; - - // Suspend a user by putting the user into read-only mode for a period. - Firechat.prototype.suspendUser = function(userId, timeLengthSeconds, cb) { - var self = this, - suspendedUntil = Firebase.ServerValue.TIMESTAMP + 1000*timeLengthSeconds; - - self._suspensionsRef.child(userId).set(suspendedUntil, function(error) { - if (error && cb) { - return cb(error); - } else { - self.sendSuperuserNotification(userId, 'suspension', { - suspendedUntil: suspendedUntil - }); - return cb(null); - } - }); - }; - - // Invite a user to a specific chat room. - Firechat.prototype.inviteUser = function(userId, roomId) { - var self = this, - sendInvite = function() { - var inviteRef = self._firebase.child('users').child(userId).child('invites').push(); - inviteRef.set({ - id: inviteRef.name(), - fromUserId: self._userId, - fromUserName: self._userName, - toRoomId: roomId - }); - - // Handle listen unauth / failure in case we're kicked. - inviteRef.on('value', self._onFirechatInviteResponse, function(){}, self); - }; - - self.getRoom(roomId, function(room) { - if (room.type === 'private') { - var authorizedUserRef = self._roomRef.child(roomId).child('authorizedUsers'); - authorizedUserRef.child(userId).set(true, function(error) { - if (!error) { - sendInvite(); - } - }); - } else { - sendInvite(); - } - }); - }; - - Firechat.prototype.acceptInvite = function(inviteId, cb) { - var self = this; - - self._userRef.child('invites').child(inviteId).once('value', function(snapshot) { - var invite = snapshot.val(); - if (invite === null && cb) { - return cb(new Error('acceptInvite(' + inviteId + '): invalid invite id')); - } else { - self.enterRoom(invite.toRoomId); - self._userRef.child('invites').child(inviteId).update({ - 'status': 'accepted', - 'toUserName': self._userName - }, cb); - } - }, self); - }; - - Firechat.prototype.declineInvite = function(inviteId, cb) { - var self = this, - updates = { - 'status': 'declined', - 'toUserName': self._userName - }; - - self._userRef.child('invites').child(inviteId).update(updates, cb); - }; - - Firechat.prototype.getRoomList = function(cb) { - var self = this; - - self._roomRef.once('value', function(snapshot) { - cb(snapshot.val()); - }); - }; - - Firechat.prototype.getUsersByRoom = function() { - var self = this, - roomId = arguments[0], - query = self._firebase.child('room-users').child(roomId), - cb = arguments[arguments.length - 1], - limit = null; - - if (arguments.length > 2) { - limit = arguments[1]; - } - - query = (limit) ? query.limit(limit) : query; - - query.once('value', function(snapshot) { - var usernames = snapshot.val() || {}, - usernamesUnique = {}; - - for (var username in usernames) { - for (var session in usernames[username]) { - // Skip all other sessions for this user as we only need one. - usernamesUnique[username] = usernames[username][session]; - break; - } - } - - root.setTimeout(function() { - cb(usernamesUnique); - }, 0); - }); - }; - - Firechat.prototype.getUsersByPrefix = function(prefix, startAt, endAt, limit, cb) { - var self = this, - query = this._usersOnlineRef, - prefixLower = prefix.toLowerCase(); - - if (startAt) { - query = query.startAt(null, startAt); - } else if (endAt) { - query = query.endAt(null, endAt); - } else { - query = (prefixLower) ? query.startAt(null, prefixLower) : query.startAt(); - } - - query = (limit) ? query.limit(limit) : query; - - query.once('value', function(snapshot) { - var usernames = snapshot.val() || {}, - usernamesFiltered = {}; - - for (var userNameKey in usernames) { - var sessions = usernames[userNameKey], - userName, userId, usernameClean; - - // Grab the user data from the first registered active session. - for (var sessionId in sessions) { - userName = sessions[sessionId].name; - userId = sessions[sessionId].id; - - // Skip all other sessions for this user as we only need one. - break; - } - - // Filter out any usernames that don't match our prefix and break. - if ((prefix.length > 0) && (userName.toLowerCase().indexOf(prefixLower) !== 0)) - continue; - - usernamesFiltered[userName] = { - name: userName, - id: userId - }; - } - - root.setTimeout(function() { - cb(usernamesFiltered); - }, 0); - }); - }; - - // Miscellaneous helper methods. - Firechat.prototype.getRoom = function(roomId, callback) { - this._roomRef.child(roomId).once('value', function(snapshot) { - callback(snapshot.val()); - }); - }; - - Firechat.prototype.userIsModerator = function() { - return this._isModerator; - }; - - Firechat.prototype.sessionIdGet = function() { - return this._sessionId; - }; -})(Firebase); \ No newline at end of file diff --git a/src/js/libs/jquery-1.9.1.min.js b/src/js/libs/jquery-1.9.1.min.js deleted file mode 100644 index 006e953..0000000 --- a/src/js/libs/jquery-1.9.1.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map -*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
                          a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
                          t
                          ",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
                          ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},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(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; -return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
                          ",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
                          ","
                          "],area:[1,"",""],param:[1,"",""],thead:[1,"","
                          "],tr:[2,"","
                          "],col:[2,"","
                          "],td:[3,"","
                          "],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
                          ","
                          "]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) -}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("