Repository: jsfiddle/togetherjs Branch: develop Commit: d0c4254d86a1 Files: 387 Total size: 3.3 MB Directory structure: gitextract_f7j0faal/ ├── .csslint.rc ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE.html ├── Procfile ├── README.md ├── addon/ │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── data/ │ │ ├── attachment.js │ │ ├── button.html │ │ ├── button.js │ │ └── startup-help.html │ ├── lib/ │ │ ├── main.js │ │ └── startup-panel.js │ └── package.json ├── client ├── devserver.js ├── hub/ │ ├── server.js │ └── websocket-compat.js ├── package.json ├── phantomjs/ │ └── bridge.js ├── site/ │ ├── base.tmpl │ ├── bookmarklet.html │ ├── css/ │ │ ├── bootstrap.css │ │ ├── carousel.css │ │ ├── docco.css │ │ ├── grid.css │ │ ├── jumbotron.css │ │ ├── responsive-video.css │ │ └── style.css │ ├── docs/ │ │ ├── contributing.md │ │ ├── faq.md │ │ └── index.md │ ├── docs-contributing.tmpl │ ├── docs.tmpl │ ├── errors/ │ │ ├── 404.html │ │ └── 500.html │ ├── examples/ │ │ ├── drawing/ │ │ │ ├── css/ │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js/ │ │ │ └── sketch.js │ │ ├── friendlycode/ │ │ │ ├── .gitignore │ │ │ ├── .gitmodules │ │ │ ├── LICENSE.html │ │ │ ├── LICENSE_files/ │ │ │ │ ├── css.css │ │ │ │ ├── css_002.css │ │ │ │ └── css_003.css │ │ │ ├── README.md │ │ │ ├── build-require.js │ │ │ ├── codemirror2/ │ │ │ │ ├── lib/ │ │ │ │ │ ├── codemirror.css │ │ │ │ │ └── codemirror.js │ │ │ │ └── mode/ │ │ │ │ ├── css/ │ │ │ │ │ └── css.js │ │ │ │ ├── htmlmixed/ │ │ │ │ │ └── htmlmixed.js │ │ │ │ ├── javascript/ │ │ │ │ │ └── javascript.js │ │ │ │ └── xml/ │ │ │ │ └── xml.js │ │ │ ├── css/ │ │ │ │ ├── buttons.css │ │ │ │ ├── editor.css │ │ │ │ ├── errorhelp.css │ │ │ │ ├── friendlycode.css │ │ │ │ ├── jsbin-codemirror-theme.css │ │ │ │ ├── modals.css │ │ │ │ ├── modals_full_screen.css │ │ │ │ ├── opensans/ │ │ │ │ │ └── stylesheet.css │ │ │ │ ├── opensymbolcropped/ │ │ │ │ │ └── stylesheet.css │ │ │ │ ├── tipsy.css │ │ │ │ └── ubuntumono/ │ │ │ │ └── stylesheet.css │ │ │ ├── examples/ │ │ │ │ ├── alternate-publisher.html │ │ │ │ ├── bare-optimized.html │ │ │ │ ├── bare.html │ │ │ │ └── editor-only.html │ │ │ ├── index.html │ │ │ ├── js/ │ │ │ │ ├── backbone-events.js │ │ │ │ ├── fc/ │ │ │ │ │ ├── current-page-manager.js │ │ │ │ │ ├── hacktionary-data.js │ │ │ │ │ ├── help.js │ │ │ │ │ ├── parachute.js │ │ │ │ │ ├── prefs.js │ │ │ │ │ ├── publisher.js │ │ │ │ │ └── ui/ │ │ │ │ │ ├── context-sensitive-help.js │ │ │ │ │ ├── editor-panes.js │ │ │ │ │ ├── editor-toolbar.js │ │ │ │ │ ├── editor.js │ │ │ │ │ ├── error-help.js │ │ │ │ │ ├── gutter-pointer.js │ │ │ │ │ ├── history.js │ │ │ │ │ ├── indexable-codemirror.js │ │ │ │ │ ├── live-preview.js │ │ │ │ │ ├── mark-tracker.js │ │ │ │ │ ├── modals.js │ │ │ │ │ ├── parsing-codemirror.js │ │ │ │ │ ├── preview-to-editor-mapping.js │ │ │ │ │ ├── publish.js │ │ │ │ │ ├── relocator.js │ │ │ │ │ ├── social-media.js │ │ │ │ │ └── text.js │ │ │ │ ├── friendlycode.js │ │ │ │ ├── jquery.no-conflict.js │ │ │ │ ├── jquery.tipsy.js │ │ │ │ ├── lscache.js │ │ │ │ ├── require-config.js │ │ │ │ ├── require-plugins/ │ │ │ │ │ ├── template.js │ │ │ │ │ └── text.js │ │ │ │ └── slowparse-errors.js │ │ │ ├── package.json │ │ │ ├── slowparse/ │ │ │ │ ├── .gitignore │ │ │ │ ├── LICENSE.html │ │ │ │ ├── LICENSE_files/ │ │ │ │ │ ├── css.css │ │ │ │ │ ├── css_002.css │ │ │ │ │ └── css_003.css │ │ │ │ ├── README.md │ │ │ │ ├── demo/ │ │ │ │ │ ├── hierarchic-source-code.css │ │ │ │ │ ├── index.html │ │ │ │ │ ├── jsbin-codemirror-theme.css │ │ │ │ │ ├── render-dom.js │ │ │ │ │ ├── tag-colors.js │ │ │ │ │ └── utils.js │ │ │ │ ├── index.html │ │ │ │ ├── slowparse.js │ │ │ │ ├── spec/ │ │ │ │ │ ├── errors.base.html │ │ │ │ │ ├── errors.forbidjs.html │ │ │ │ │ ├── errors.jquery.js │ │ │ │ │ ├── index.html │ │ │ │ │ └── spec.js │ │ │ │ ├── test/ │ │ │ │ │ ├── index.html │ │ │ │ │ ├── qunit.css │ │ │ │ │ ├── qunit.js │ │ │ │ │ ├── test-errors.jquery.js │ │ │ │ │ ├── test-slowparse.js │ │ │ │ │ ├── test-spec.js │ │ │ │ │ ├── test-tree-inspectors.js │ │ │ │ │ └── testing-utils.js │ │ │ │ ├── tree-inspectors.js │ │ │ │ └── vendor/ │ │ │ │ ├── brocco.js │ │ │ │ ├── codemirror2/ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ ├── codemirror.css │ │ │ │ │ │ └── codemirror.js │ │ │ │ │ └── mode/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── css.js │ │ │ │ │ ├── htmlmixed/ │ │ │ │ │ │ └── htmlmixed.js │ │ │ │ │ ├── javascript/ │ │ │ │ │ │ └── javascript.js │ │ │ │ │ └── xml/ │ │ │ │ │ └── xml.js │ │ │ │ ├── docco.css │ │ │ │ ├── jump-to.js │ │ │ │ └── showdown.js │ │ │ ├── templates/ │ │ │ │ ├── confirm-dialog.html │ │ │ │ ├── default-content.html │ │ │ │ ├── error-dialog.html │ │ │ │ ├── error-msg.html │ │ │ │ ├── help-msg.html │ │ │ │ ├── nav-options.html │ │ │ │ └── publish-dialog.html │ │ │ └── test/ │ │ │ ├── all-tests.js │ │ │ ├── codemirror-577/ │ │ │ │ ├── original.html │ │ │ │ ├── replacer.html │ │ │ │ └── test-codemirror-577.js │ │ │ ├── define-tests.js │ │ │ ├── index-optimized.html │ │ │ ├── index.html │ │ │ ├── lptest.js │ │ │ ├── preview-to-editor-mapping/ │ │ │ │ ├── path-to.html │ │ │ │ └── test-preview-to-editor-mapping.js │ │ │ ├── publisher/ │ │ │ │ ├── post-publish.html │ │ │ │ ├── pre-publish.html │ │ │ │ └── test-publisher.js │ │ │ ├── qunit.css │ │ │ ├── qunit.js │ │ │ ├── test-app-optimized.html │ │ │ ├── test-app.html │ │ │ ├── test-app.js │ │ │ ├── test-current-page-manager.js │ │ │ ├── test-editor-toolbar.js │ │ │ ├── test-gutter-pointer.js │ │ │ ├── test-help.js │ │ │ ├── test-indexable-codemirror.js │ │ │ ├── test-live-preview.js │ │ │ ├── test-mark-tracker.js │ │ │ ├── test-parsing-codemirror.js │ │ │ ├── test-prefs.js │ │ │ ├── test-slowparse-errors.js │ │ │ └── test-templates.js │ │ ├── madlibs/ │ │ │ ├── css/ │ │ │ │ ├── app.css │ │ │ │ └── bootstrap.css │ │ │ ├── index.html │ │ │ └── js/ │ │ │ ├── app.js │ │ │ └── bootstrap.js │ │ ├── persona/ │ │ │ ├── index.html │ │ │ └── md5.js │ │ ├── tinymce/ │ │ │ ├── css/ │ │ │ │ └── application.css │ │ │ ├── index.html │ │ │ └── js/ │ │ │ ├── application.js │ │ │ └── application.js~ │ │ ├── todo/ │ │ │ ├── css/ │ │ │ │ └── bootstrap.css │ │ │ ├── index.html │ │ │ └── js/ │ │ │ ├── app.js │ │ │ └── bootstrap.js │ │ └── youtube/ │ │ ├── css/ │ │ │ └── application.css │ │ ├── index.html │ │ └── js/ │ │ └── application.js │ ├── faq.html │ ├── generic-markdown.tmpl │ ├── index.html │ ├── js/ │ │ ├── bootstrap.js │ │ ├── custom.js │ │ ├── frontpage.js │ │ ├── how-animations.js │ │ ├── imageresizer.js │ │ ├── parallax.js │ │ ├── retina.js │ │ ├── scrollTo.js │ │ ├── scrollspy.js │ │ └── source-code.js │ ├── less/ │ │ ├── alerts.less │ │ ├── badges.less │ │ ├── bootstrap.less │ │ ├── breadcrumbs.less │ │ ├── button-groups.less │ │ ├── buttons.less │ │ ├── carousel.less │ │ ├── close.less │ │ ├── code.less │ │ ├── component-animations.less │ │ ├── dropdowns.less │ │ ├── forms.less │ │ ├── grid.less │ │ ├── input-groups.less │ │ ├── jumbotron.less │ │ ├── labels.less │ │ ├── list-group.less │ │ ├── media.less │ │ ├── mixins.less │ │ ├── modals.less │ │ ├── navbar.less │ │ ├── navs.less │ │ ├── normalize.less │ │ ├── pager.less │ │ ├── pagination.less │ │ ├── panels.less │ │ ├── popovers.less │ │ ├── print.less │ │ ├── progress-bars.less │ │ ├── responsive-utilities.less │ │ ├── retina.less │ │ ├── scaffolding.less │ │ ├── tables.less │ │ ├── thumbnails.less │ │ ├── tooltip.less │ │ ├── type.less │ │ ├── utilities.less │ │ ├── variables.less │ │ └── wells.less │ ├── source-code-index.tmpl │ ├── source-code.tmpl │ └── the-developers/ │ ├── index.html │ ├── the-developers.css │ └── the-developers.js └── togetherjs/ ├── README.md ├── analytics.js ├── channels.js ├── chat.js ├── console.js ├── cursor.js ├── elementFinder.js ├── eventMaker.js ├── forms.js ├── functions.less ├── help.txt ├── images/ │ └── notification.ogg ├── interface.html ├── jqueryPlugins.js ├── libs/ │ ├── almond.js │ ├── require-nomin.js │ ├── require.js │ ├── tinycolor.js │ ├── walkabout/ │ │ ├── README.md │ │ ├── index.html │ │ ├── lib/ │ │ │ ├── esprima.js │ │ │ └── falafel.js │ │ ├── node-proxy.js │ │ ├── test_overlap.html │ │ ├── test_overlap.js │ │ ├── test_walkabout.html │ │ ├── test_walkabout.js │ │ ├── test_walkabout_nojquery.html │ │ ├── test_walkabout_nojquery.js │ │ ├── traverse-rewrite.js │ │ └── walkabout.js │ └── whrandom/ │ ├── README.md │ ├── mersenne.js │ ├── random.js │ ├── test_random.html │ └── test_random.js ├── linkify.js ├── locale/ │ ├── de-DE.json │ ├── en-US.json │ ├── es-BO.json │ ├── pl-PL.json │ └── ru.json ├── mobile.less ├── module-descriptions.json ├── ot.js ├── peers.js ├── playback.js ├── randomutil.js ├── recorder.html ├── recorder.js ├── recorder.less ├── reset.less ├── session.js ├── startup.js ├── storage.js ├── templates-localized.js ├── templates.js ├── templating.js ├── tests/ │ ├── ace.js │ ├── codemirror4.js │ ├── doctestjs/ │ │ ├── .gitignore │ │ ├── .gitmodules │ │ ├── .hgignore │ │ ├── .resources/ │ │ │ ├── CNAME │ │ │ ├── boilerplate/ │ │ │ │ ├── 404.html │ │ │ │ ├── css/ │ │ │ │ │ ├── main.css │ │ │ │ │ └── normalize.css │ │ │ │ ├── index.html │ │ │ │ └── js/ │ │ │ │ └── main.js │ │ │ ├── doc.css │ │ │ ├── example.xml │ │ │ ├── footer.html │ │ │ ├── header.html │ │ │ ├── include-scripts.sh │ │ │ ├── retemplate.py │ │ │ ├── template.html │ │ │ ├── toc.js │ │ │ └── try.js │ │ ├── .syncignore │ │ ├── README.md │ │ ├── bin/ │ │ │ └── doctest │ │ ├── doctest.css │ │ ├── doctest.js │ │ ├── examples/ │ │ │ ├── examples-2.html │ │ │ ├── examples-2.js │ │ │ ├── examples.html │ │ │ ├── long-running-tests.html │ │ │ └── long-running-tests.js │ │ ├── index.html │ │ ├── package.json │ │ ├── reference.html │ │ ├── try.html │ │ └── tutorial.html │ ├── func_ace.js │ ├── func_codemirror.js │ ├── func_forms.js │ ├── func_misc.js │ ├── func_notifications.js │ ├── func_peer_status.js │ ├── func_walkthrough.js │ ├── index.html │ ├── interactive.js │ ├── manual/ │ │ ├── index.html │ │ ├── multi-textarea-focus.html │ │ └── youtube-video.html │ ├── mobiletest.html │ ├── test_console.js │ ├── test_elementFinder.js │ ├── test_linkify.js │ ├── test_misc.js │ ├── test_ot.js │ ├── test_ot_text.js │ ├── test_resolves.js │ ├── test_storage.js │ ├── testutils.js │ └── togetherjs-animations/ │ ├── css/ │ │ └── styles.css │ ├── index.html │ └── js/ │ └── animations.js ├── togetherjs.js ├── togetherjs.less ├── ui.js ├── util.js ├── videos.js ├── visibilityApi.js ├── walkabout.html ├── walkthrough.html ├── walkthrough.js ├── webrtc.js ├── who.js ├── windowing.js └── youtubeVideos.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .csslint.rc ================================================ { "empty-rules": false, "empty-rules-reason": "LESS creates some empty rules", "bulletproof-font-face": false, "buttetproof-font-face-reason": "This only seems to apply when you provide multiple formats, and we aren't doing that", "qualified-headings": false, "qualified-headings-reason": "This happens as part of the reset, we reset headings inside .towtruck (though technically we aren't using headings so we could remove that reset)", "adjoining-classes": false, "adjoining-classes-reason": ".class1.class2 isn't supported in some older browsers, but we don't support those", "vendor-prefix": false, "vendor-prefix-reason": "We have some vendor prefixes in vendor-prefix-specific selectors, and CSSLint complains about that.", "compatible-vendor-prefixes": false, "compatible-vendor-prefixes-reason": "Same as vendor-prefix", "duplicate-background-images": false, "duplicate-background-images-reason": "LESS explodes out some background images, creating duplicates, but they aren't duplicated in the source.", "fallback-colors": false, "fallback-colors-reason": "Some old browsers don't support RGBA, but we don't care about those browsers" } ================================================ FILE: .gitignore ================================================ node_modules .DS_Store .env* addon/Profile dump.rdb app/http/public/recorder.css build test-build togetherjs.mozillalabs.com addon/togetherjs.xpi togetherjs/togetherjs.css togetherjs/togetherjsPackage.js ================================================ FILE: .jshintrc ================================================ { // JSHint configurations for node-jshint. // install with "npm install -g jshint" // run with "jshint --config config.json" "asi" : false, // Semicolons required! we minify. "undef" : false, // var early, var often // Predefined globals whom JSHint will accept the existence of. "browser" : true, // Require curly braces "curly": true, // Require hasOwnProperty check "forin": true, "indent": 2, // No arguments.callee/caller "noarg": true, // Warn about unused variables "unused": true, // No trailing whitespace "trailing": true } ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "0.10" before_install: - npm install -g npm - npm install -g grunt-cli ================================================ FILE: CONTRIBUTING.md ================================================ Thanks for your interest in contributing to TogetherJS! If you found a bug, if at all possible give us a URL where we can try TogetherJS. Don't worry about tags or milestones or assigning the ticket. But a URL is extremely helpful! ## Contributing The [Contributing](https://togetherjs.com/docs/contributing.html) document on the site gives some more information. Some relevant points: * [Javascript style guide](https://github.com/ianb/javascript) * If you want to work on a ticket, please leave a comment to that effect. It gives us a chance to suggest where you'd look in the code to implement the feature or fix the bug, and makes it less likely that people's contributions will conflict. * Anything in the [Blue Sky](https://github.com/mozilla/togetherjs/issues?milestone=23&page=1&state=open) you are likely to find something that we're interested in having in TogetherJS, but that we aren't working on. * If you have an idea of your own you'd like to implement, please open a ticket describing it. That will let other people know you are working on it, and give other people an opportunity to give feedback or implementation notes. ## Where to start? You should look at the [Contribution Wanted](https://github.com/mozilla/togetherjs/issues?labels=contribution-wanted&milestone=&page=1&state=open) tag to see tickets that fall into two categories: 1. A good introductory task to get started on. 2. Something that requires particular skills (that the core team does not have) that would make a contribution particular valuable. ================================================ FILE: Gruntfile.js ================================================ /*jshint forin:false */ var fs = require("fs"); var path = require('path'); var nunjucks = require("nunjucks"); var marked = require("marked"); var docco = require("docco"); var vars = { enableExample: false, enableHome: false, GA_ACCOUNT: "UA-35433268-28", base: "" }; var TESTDIR = "test-build"; module.exports = function (grunt) { if (! grunt.option("dest")) { grunt.option("dest", "build"); } var dumpLineNumbers = false; if (grunt.option("less-line-numbers")) { grunt.verbose.writeln("Enabling LESS line numbers"); dumpLineNumbers = true; } function copyLink(src, dest) { if (grunt.file.isDir(src)) { grunt.file.mkdir(dest); return; } var destDir = path.dirname(dest); if (! grunt.file.exists(destDir)) { grunt.file.mkdir(destDir); } if (! grunt.option("no-hardlink")) { try { if (grunt.file.exists(dest)) { grunt.file.delete(dest); } fs.linkSync(src, dest); } catch (e) { grunt.file.copy(src, dest); } } else { grunt.file.copy(src, dest); } } function copyMany(src, dest, patterns) { var paths = grunt.file.expand({cwd: src}, patterns); paths.forEach(function (p) { var srcPath = path.join(src, p); var destPath = path.join(dest, p); copyLink(srcPath, destPath); }); } var libs = []; grunt.file.expand( ["togetherjs/*.js", "!togetherjs/randomutil.js", "!togetherjs/recorder.js", "!togetherjs/togetherjs.js"] ).forEach(function (filename) { filename = filename.replace(/^togetherjs\//, ""); filename = filename.replace(/\.js$/, ""); libs.push(filename); }); var langs = []; grunt.file.expand("togetherjs/locale/*.json").forEach(function (langFilename) { var lang = path.basename(langFilename).replace(/\.json/, ""); langs.push(lang); libs.push("templates-" + lang); }); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), less: { development: { files: { "<%= grunt.option('dest') || 'build' %>/togetherjs/togetherjs.css": "togetherjs/togetherjs.less", "<%= grunt.option('dest') || 'build' %>/togetherjs/recorder.css": "togetherjs/recorder.less" }, options: { dumpLineNumbers: dumpLineNumbers } } }, requirejs: { compile: { options: { baseUrl: "togetherjs/", //paths: requirejsPaths, include: ["libs/almond"].concat(libs), //Wrap any build bundle in a start and end text specified by wrap. //Use this to encapsulate the module code so that define/require are //not globals. The end text can expose some globals from your file, //making it easy to create stand-alone libraries that do not mandate //the end user use requirejs. wrap: { start: "(function() {", end: "TogetherJS.require = TogetherJS._requireObject = require;\nTogetherJS._loaded = true;\nrequire([\"session\"]);\n}());" }, optimize: "none", out: function writer(text) { var dest = path.join(grunt.option("dest"), "togetherjs/togetherjsPackage.js"); grunt.file.write(dest, text); } } } }, jshint: { options: { curly: true, browser: true, globals: { define: true } }, all: [ "Gruntfile", "togetherjs/*.js" ] }, csslint: { // Check here for options: https://github.com/stubbornella/csslint/wiki/Rules options: { csslintrc: ".csslint.rc" }, src: [path.join(grunt.option("dest"), "togetherjs/togetherjs.css")] }, watch: { main: { files: ["togetherjs/**/*", "Gruntfile.js"], tasks: ["build"], options: { nospawn: true } }, site: { files: ["togetherjs/**/*", "Gruntfile.js", "site/**/*", "!**/*_flymake*", "!**/*~", "!**/.*"], tasks: ["build", "buildsite"] }, // FIXME: I thought I wouldn't have to watch for // togetherjs/**/*.js, but because the hard links are regularly // broken by git, this needs to be run often, and it's easy to // forget. Then between git action the build will be over-run, // but that's harmless. minimal: { files: ["togetherjs/**/*.less", "togetherjs/togetherjs.js", "togetherjs/templates-localized.js", "togetherjs/**/*.html", "togetherjs/**/*.js", "!**/*_flymake*", "togetherjs/locales/**/*.json"], tasks: ["build"] } }, 'http-server': { 'test': { // the server root directory root: '.', cache: 30, //showDir: true, //autoIndex: true, // run in parallel with other tasks runInBackground: true } }, 'phantom-tests': grunt.file.expand({ cwd:"togetherjs/tests/" }, "test_*.js", "func_*.js", "interactive.js", "!test_ot.js"). reduce(function(o, k) { o[k] = {}; return o; }, {}) }); grunt.loadNpmTasks("grunt-contrib-less"); grunt.loadNpmTasks("grunt-contrib-csslint"); grunt.loadNpmTasks("grunt-contrib-jshint"); grunt.loadNpmTasks("grunt-contrib-requirejs"); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.registerTask("config-requirejs", function() { // configure the requirejs paths based on the current options var requirejsPaths = { jquery: "libs/jquery-1.11.1.min", walkabout: "libs/walkabout/walkabout", esprima: "libs/walkabout/lib/esprima", falafel: "libs/walkabout/lib/falafel", tinycolor: "libs/tinycolor", whrandom: "libs/whrandom/random", jqueryui: "libs/jquery-ui.min", jquerypunch: "libs/jquery.ui.touch-punch.min", // Make sure we get the built form of this one: templates: path.join("..", grunt.option("dest"), "togetherjs/templates") }; langs.forEach(function(lang) { requirejsPaths["templates-" + lang] = path.join("..", grunt.option("dest"), "togetherjs", "templates-" + lang); }); grunt.config.merge({ requirejs: { compile: { options: { paths: requirejsPaths } } } }); grunt.task.run("requirejs"); }); grunt.registerTask("copylib", "copy the library", function () { var pattern = ["**", "!togetherjs.js", "!templates-localized.js", "!**/*.less", "!#*", "!**/*_flymake*", "!**/*.md", "!**/*.tmp", "!**/#*"]; grunt.log.writeln("Copying files from " + "togetherjs/".cyan + " to " + path.join(grunt.option("dest"), "togetherjs").cyan); if (grunt.option("exclude-tests")) { pattern.push("!tests/"); pattern.push("!tests/**"); grunt.log.writeln(" (excluding tests)"); } copyMany( "togetherjs/", path.join(grunt.option("dest"), "togetherjs"), pattern ); }); grunt.registerTask("copysite", "copy the site (not library)", function () { grunt.log.writeln("Copying files from " + "site/".cyan + " to " + grunt.option("dest").cyan); copyMany( "site/", grunt.option("dest"), ["**", "!**/*.tmpl", "!**/*.html", "!public/**", "!**/*_flymake*", "!**/*.md"]); copyMany( "site/public/", grunt.option("dest"), ["**"]); }); grunt.registerTask("build", ["copylib", "maybeless", "substitute", "config-requirejs"]); grunt.registerTask("buildsite", ["copysite", "render", "rendermd", "docco"]); grunt.registerTask("devwatch", ["build", "watch:minimal"]); // For some reason doing ["build", "buildsite", "watch:site"] // doesn't work, it gets through buildsite and doesn't watch; // instead just doing watch:site seems okay: grunt.registerTask("sitewatch", ["buildsite", "watch:site"]); function escapeString(s) { if (typeof s != "string") { throw new Error("Not a string: " + s); } var data = JSON.stringify(s); return data.substr(1, data.length-2); } grunt.registerTask( "substitute", "Substitute templates-localized.js and parameters in togetherjs.js", function () { // FIXME: I could use grunt.file.copy(..., {process: function (content, path) {}}) here var baseUrl = grunt.option("base-url") || ""; // baseURL to be entered by the user if (! baseUrl) { grunt.log.writeln("No --base-url, using auto-detect"); } var destBase = grunt.option("dest") || "build"; // where to put the built files. If not indicated then into build/ var hubUrl = grunt.option("hub-url") || process.env.HUB_URL || "https://hub.togetherjs.com"; // URL of the hub server grunt.log.writeln("Using hub URL " + hubUrl.cyan); var gitCommit = process.env.GIT_COMMIT || ""; var subs = { __interface_html__: grunt.file.read("togetherjs/interface.html"), __help_txt__: grunt.file.read("togetherjs/help.txt"), __walkthrough_html__: grunt.file.read("togetherjs/walkthrough.html"), __baseUrl__: baseUrl, __hubUrl__: hubUrl, __gitCommit__: gitCommit }; function substituteContent(content, s) { for (var v in s) { var re = new RegExp(v, "g"); if (typeof s[v] != "string") { grunt.log.error("Substitution variable " + v.cyan + " is not a string") } content = content.replace(re, escapeString(s[v])); } return content; } var filenames = { "togetherjs.js": { src: "togetherjs/togetherjs.js", extraVariables: {__min__: "no"} }, "togetherjs-min.js": { src: "togetherjs/togetherjs.js", extraVariables: {__min__: "yes"} } }; for (var dest in filenames) { var info = filenames[dest]; var src = info.src; var extraVariables = info.extraVariables; dest = destBase + "/" + dest; var content = fs.readFileSync(src, "UTF-8"); var s = subs; if (extraVariables) { s = Object.create(subs); for (var a in extraVariables) { s[a] = extraVariables[a]; } } content = substituteContent(content, s); grunt.log.writeln("writing " + src.cyan + " to " + dest.cyan); grunt.file.write(dest, content); } grunt.file.expand("togetherjs/locale/*.json").forEach(function (langFilename) { var templates = grunt.file.read("togetherjs/templates-localized.js"); var lang = path.basename(langFilename).replace(/\.json/, ""); var translation = JSON.parse(grunt.file.read(langFilename)); var dest = path.join(grunt.option("dest"), "togetherjs/templates-" + lang + ".js"); var translatedInterface = translateFile("togetherjs/interface.html", translation); var translatedHelp = translateFile("togetherjs/help.txt", translation); var translatedWalkthrough = translateFile("togetherjs/walkthrough.html", translation); var vars = subs; subs.__interface_html__ = translatedInterface; subs.__help_txt__ = translatedHelp; subs.__walkthrough_html__ = translatedWalkthrough; subs.__names__ = translation.names; templates = substituteContent(templates, subs); grunt.file.write(dest, templates); grunt.log.writeln("writing " + dest.cyan + " based on " + langFilename.cyan); }); return true; } ); function translateFile(source, translation) { var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("./")); var tmpl = env.getTemplate(source); return tmpl.render({ gettext: function (string) { return translation[string] || string; } }); } grunt.registerTask("maybeless", "Maybe compile togetherjs.less", function () { var sources = grunt.file.expand(["togetherjs/**/*.less", "site/**/*.less"]); var found = false; sources.forEach(function (fn) { var source = fs.statSync(fn); var destFn = grunt.option("dest") + "/" + fn.substr(0, fn.length-4) + "css"; if (! fs.existsSync(destFn)) { found = true; return; } var dest = fs.statSync(destFn); if (source.mtime.getTime() > dest.mtime.getTime()) { grunt.log.writeln("Destination LESS out of date: " + destFn.cyan); found = true; } }); if (found) { grunt.task.run("less"); } else { grunt.log.writeln("No .less files need regenerating."); } }); grunt.registerTask("render", "Render the site", function () { var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("site/")); var sources = grunt.file.expand({cwd: "site/"}, "**/*.html"); sources.forEach(function (source) { var dest = grunt.option("dest") + "/" + source; grunt.log.writeln("Rendering " + source.cyan + " to " + dest.cyan); var data = grunt.file.read("site/" + source); var tmplVars = Object.create(vars); while (true) { var match = /\{\#\s+set\s+([^\s]+)\s+([^#]+)\s*\#\}/.exec(data); if (! match) { break; } tmplVars[match[1]] = JSON.parse(match[2]); grunt.log.writeln(" Found variable " + match[1] + " = " + match[2]); data = data.substr(match.index + match[0].length); } tmplVars.base = path.relative(path.dirname("site/" + source), "site/"); if (tmplVars.base && tmplVars.base.search(/\/$/) == -1) { tmplVars.base += "/"; } if (tmplVars.absoluteLinks) { tmplVars.base = "/"; } tmplVars.base = tmplVars.base.replace(/\\/g, '/'); var tmpl = env.getTemplate(source); var result = tmpl.render(tmplVars); grunt.file.write(dest, result); }); }); function parseMarkdownOutput(doc) { var title = (/]*>(.*)<\/h1>/i).exec(doc); title = title[1]; var body = doc.replace(/]*>.*<\/h1>/i, ""); return { title: title, body: body }; } function addHeaderIds(doc) { var result = []; while (doc) { var match = (/((.*)(<\/h\d>)/i).exec(doc); if (! match) { result.push(doc); break; } var id = match[2]; id = id.toLowerCase(); id = id.replace(/ +/g, "-"); id = id.replace(/[^a-z0-9_\-]/g, ""); var header = match[1] + ' id="' + id + '">' + match[2] + match[3]; result.push(doc.substr(0, match.index)); result.push(header); doc = doc.substr(match.index + match[0].length); } return result.join(""); } function highlight(code, lang) { var hjs = require("highlight.js"); var aliases = { html: "xml", js: "javascript" }; lang = aliases[lang] || lang; try { if (lang) { return hjs.highlight(lang, code).value; } else { return hjs.highlightAuto(code).value; } } catch (e) { grunt.fail.fatal("Error highlighting: " + e); throw e; } } marked.setOptions({highlight: highlight}); grunt.registerTask("rendermd", "Render the site Markdown files", function () { var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("site/")); var sources = grunt.file.expand({cwd: "site/"}, "**/*.md", "!**/README.md"); sources.forEach(function (source) { var basename = source.replace(/\.md$/, ""); var dest = grunt.option("dest") + "/" + basename + ".html"; grunt.log.writeln("Rendering " + source.cyan + " to " + dest.cyan); var data = grunt.file.read("site/" + source); var templateName = "generic-markdown.tmpl"; var match = (/template:\s*([a-zA-Z_\-0-9.]*)/).exec(data); if (match) { templateName = match[1]; } var html = marked(data, { smartypants: true }); var parsed = parseMarkdownOutput(html); parsed.body = addHeaderIds(parsed.body); var tmpl = env.getTemplate(templateName); var tmplVars = Object.create(vars); tmplVars.markdownBody = parsed.body; tmplVars.title = parsed.title; tmplVars.base = path.relative(path.dirname("site/" + source), "site/"); if (tmplVars.base && tmplVars.base.search(/\/$/) == -1) { tmplVars.base += "/"; } tmplVars.base = tmplVars.base.replace(/\\/g, '/'); var result = tmpl.render(tmplVars); grunt.file.write(dest, result); }); }); function doccoFormat(source, sections) { sections.forEach(function (section) { var code = highlight(section.codeText, "javascript"); code = code.replace(/\s+$/, ''); section.codeHtml = "
" + code + "
"; section.docsHtml = marked(section.docsText); }); } grunt.registerTask("docco", "Create comment-separating source code", function () { var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("site/")); var sources = grunt.file.expand({cwd: "togetherjs/"}, "*.js"); sources.sort(); var sourceDescriptions = JSON.parse(grunt.file.read("togetherjs/module-descriptions.json")); var sourceList = []; sources.forEach(function (source) { var name = source.replace(/\.js$/, ""); sourceList.push({ name: name, link: source + ".html", description: marked(sourceDescriptions[name] || "", {smartypants: true}) }); }); sources.forEach(function (source) { var sourceName = source.replace(/\.js$/, ""); var dest = grunt.option("dest") + "/source/" + source + ".html"; grunt.log.writeln("Rendering " + source.cyan + " to " + dest.cyan); var code = grunt.file.read("togetherjs/" + source); var sections = docco.parse(source, code, {languages:{}}); doccoFormat(source, sections); sections.forEach(function (section, i) { section.index = i; section.empty = section.codeText.replace(/\s/gm, "") === ""; }); var first = marked.lexer(sections[0].docsText)[0]; var hasTitle = first && first.type == 'heading' && first.depth == 1; var title = hasTitle ? first.text : path.basename(source, ".js"); var tmpl = env.getTemplate("source-code.tmpl"); var tmplVars = Object.create(vars); tmplVars.title = title; tmplVars.sections = sections; tmplVars.source = source; tmplVars.sourceName = sourceName; tmplVars.sourceDescription = marked(sourceDescriptions[sourceName] || "", {smartypants: true}); tmplVars.base = "../"; tmplVars.sourceList = sourceList; var result = tmpl.render(tmplVars); grunt.file.write(dest, result); }); var tmplVars = Object.create(vars); tmplVars.title = "TogetherJS Source Code"; tmplVars.sourceList = sourceList; tmplVars.base = "../"; var tmpl = env.getTemplate("source-code-index.tmpl"); grunt.file.write(grunt.option("dest") + "/source/index.html", tmpl.render(tmplVars)); }); grunt.registerTask("buildaddon", "Build the Firefox addon and move the XPI into the site", function () { var done = this.async(); grunt.util.spawn({ cmd: "cfx", args: ["xpi"], opts: { cwd: "addon/" } }, function (error, result, code) { if (error) { grunt.log.error("Error running cfx xpi: " + error.toString().cyan); grunt.fail.fatal("Error creating XPI"); done(); return; } var dest = path.join(grunt.option("dest"), "togetherjs.xpi"); grunt.file.copy("addon/togetherjs.xpi", dest); grunt.log.writeln("Created " + dest.cyan); done(); }); }); grunt.registerTask("publish", "Publish to togetherjs.mozillalabs.com/public/", function () { if (! grunt.file.isDir("togetherjs.mozillalabs.com")) { grunt.log.writeln("Error: you must check out togetherjs.mozillalabs.com"); grunt.log.writeln("Use:"); grunt.log.writeln(" $ git clone -b togetherjs.mozillalabs.com git:git@github.com:mozilla/togetherjs.git togetherjs.mozillalabs.com"); grunt.log.writeln(" $ cd togetherjs.mozillalabs.com/.git"); grunt.log.writeln(" $ echo '[remote \"staging\"]\n\turl = git@heroku.com:togetherjs-staging.git\n\tpush = refs/heads/togetherjs.mozillalabs.com:refs/heads/master\n[remote \"production\"]\n\turl = git@heroku.com:togetherjs.git\n\tpush = refs/heads/togetherjs.mozillalabs.com:refs/heads/master\n' >> config"); grunt.fail.fatal("Must checkout togetherjs.mozillalabs.com"); return; } var versions = "togetherjs.mozillalabs.com/public/versions"; if (! grunt.file.isDir(versions)) { grunt.log.writeln("Error: " + versions.cyan + " does not exist"); grunt.fail.fatal("No versions/ directory"); return; } var tmp = "togetherjs.mozillalabs.com/public_versions_tmp"; fs.rename(versions, tmp); grunt.file.delete("togetherjs.mozillalabs.com/public"); grunt.file.mkdir("togetherjs.mozillalabs.com/public"); fs.rename(tmp, versions); if (! grunt.option("base-url")) { grunt.option("base-url", "https://togetherjs.com"); } grunt.option("dest", "togetherjs.mozillalabs.com/public"); grunt.option("exclude-tests", true); grunt.option("no-hardlink", true); grunt.task.run(["build", "buildsite", "buildaddon"]); grunt.task.run(["movecss"]); grunt.log.writeln("To actually publish you must do:"); grunt.log.writeln(" $ cd togetherjs.mozillalabs.com/"); grunt.log.writeln(" $ git add -A"); grunt.log.writeln(" $ git commit -a -m 'Publish'"); grunt.log.writeln(" $ git push && git push staging"); }); grunt.registerTask("publishversion", "Publish to togetherjs.mozillalabs.com/public/versions/", function () { var version = grunt.option("togetherjs-version"); if (! version) { grunt.log.error("You must provide a --togetherjs-version=X.Y argument"); grunt.fail.fatal("No --togetherjs-version"); return; } if (! grunt.file.isDir("togetherjs.mozillalabs.com/public/versions")) { grunt.log.error("The directory togetherjs.mozillalabs.com/public/versions does not exist"); grunt.fail.fatal(); return; } var destDir = "togetherjs.mozillalabs.com/public/versions/" + version; if (grunt.file.exists(destDir)) { grunt.log.error("The directory " + destDir + " already exists"); grunt.log.error(" Delete it first to re-create version"); grunt.fail.fatal(); return; } grunt.option("base-url", "https://togetherjs.com/versions/" + version); grunt.option("dest", destDir); grunt.option("exclude-tests", true); grunt.option("no-hardlink", true); grunt.task.run(["build"]); grunt.task.run(["movecss"]); var readme = grunt.file.read("togetherjs.mozillalabs.com/public/versions/README.md"); readme += " * [" + version + "](./" + version + "/togetherjs.js)\n"; grunt.file.write("togetherjs.mozillalabs.com/public/versions/README.md", readme); }); grunt.registerTask("movecss", "Publish generated css files to dest", function () { // Can't figure out how to parameterize the less task, hence this lame move ["togetherjs/togetherjs.css", "togetherjs/recorder.css"].forEach(function (css) { var src = path.join("build", css); var dest = path.join(grunt.option("dest"), css); grunt.file.copy(src, dest); grunt.log.writeln("Copying " + src.cyan + " to " + dest.cyan); }); }); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('dev', function() { grunt.util.spawn({ cmd: 'node', args: ['devserver.js'] }); grunt.task.run('watch'); }); grunt.registerTask("test", "Run jshint and test suite", ["jshint", "phantom"]); grunt.loadNpmTasks('grunt-http-server'); grunt.registerTask("phantom", ["phantom-setup", "phantom-tests"]); grunt.registerTask("phantom-setup", "Run jdoctest test suite in phantomjs", function() { var done = this.async(); // find unused ports for web server and hub var freeport = require("freeport"); freeport(function(err1, hubPort) { freeport(function(err2, webPort) { if (err1 || err2) { return done(err1 || err2); } // build togetherjs using these default ports grunt.option("base-url", "http://localhost:"+webPort+"/"+TESTDIR+"/"); grunt.option("hub-url", "http://localhost:"+hubPort); grunt.option("no-hardlink", true); grunt.option("dest", TESTDIR); // make sure the web server will use the right port grunt.config.merge({ 'http-server': { test: { port: webPort, host: "localhost" } } }); // spawn a hub, using the hub port var hub = require("./hub/server"); hub.startServer(hubPort, "localhost"); // build & start the web server grunt.task.run("build", "http-server:test"); // ok, now we can run the tests in phantomjs! done(); }); }); }); // PhantomJS event handlers var phantomjs = require("grunt-lib-phantomjs").init(grunt); var phantomStatus; phantomjs.on('fail.load', function(url) { phantomjs.halt(); grunt.verbose.write('Running PhantomJS...').or.write('...'); grunt.log.error('PhantomJS unable to load "' + url + '" URI.'); phantomStatus.failed += 1; phantomStatus.total += 1; }); phantomjs.on('fail.timeout', function() { phantomjs.halt(); grunt.log.writeln(); grunt.log.error('PhantomJS timed out.'); phantomStatus.failed += 1; phantomStatus.total += 1; }); phantomjs.on('doctestjs.pass', function(result) { phantomStatus.total += 1; grunt.verbose.ok("Passed: "+result.example.summary); }); phantomjs.on('doctestjs.fail', function(result) { phantomStatus.failed += 1; phantomStatus.total += 1; grunt.log.error("Failed: "+result.example.expr); grunt.log.subhead("Expected:"); grunt.log.writeln(result.example.expected); grunt.log.subhead("Got:"); grunt.log.writeln(result.got); }); phantomjs.on('doctestjs.end', function() { phantomjs.halt(); }); // Pass through console.log statements (when verbose) phantomjs.on('console', grunt.verbose.writeln); grunt.registerMultiTask("phantom-tests", function() { grunt.task.requires('phantom-setup'); var url = grunt.option('base-url') + "togetherjs/tests/index.html?name=" + this.target; grunt.verbose.writeln("Running tests at: "+url); // Merge task-specific and/or target-specific options with these defaults. var options = this.options({ // PhantomJS timeout, in ms. timeout: 10000, // JDoctest-PhantomJS bridge file to be injected. inject: path.join(__dirname, 'phantomjs', 'bridge.js'), //screenshot: true, page: { // leave room for the togetherjs sidebar viewportSize: { width: 1024, height: 1024 } } }); // Reset test status phantomStatus = {failed: 0, passed: 0, total: 0, start: Date.now()}; // Start phantomjs on this URL var done = this.async(); phantomjs.spawn(url, { options: options, done: function() { var duration = Date.now() - phantomStatus.start; // Log results. if (phantomStatus.failed > 0) { grunt.warn(phantomStatus.failed + '/' + phantomStatus.total + ' assertions failed (' + duration + 'ms)'); } else if (phantomStatus.total === 0) { grunt.warn('0/0 assertions ran (' + duration + 'ms)'); } else { grunt.verbose.writeln(); grunt.log.ok(phantomStatus.total + ' assertions passed (' + duration + 'ms)'); } // All done! done(); } }); }); grunt.registerTask('default', 'start'); }; ================================================ FILE: LICENSE.html ================================================ Mozilla Public License, version 2.0

Mozilla Public License
Version 2.0

1. Definitions

1.1. “Contributor”

means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.

1.2. “Contributor Version”

means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution.

1.3. “Contribution”

means Covered Software of a particular Contributor.

1.4. “Covered Software”

means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.

1.5. “Incompatible With Secondary Licenses”

means

  1. that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or

  2. that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.

1.6. “Executable Form”

means any form of the work other than Source Code Form.

1.7. “Larger Work”

means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.

1.8. “License”

means this document.

1.9. “Licensable”

means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.

1.10. “Modifications”

means any of the following:

  1. any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or

  2. any new file in Source Code Form that contains any Covered Software.

1.11. “Patent Claims” of a Contributor

means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.

1.12. “Secondary License”

means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.

1.13. “Source Code Form”

means the form of the work preferred for making modifications.

1.14. “You” (or “Your”)

means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.

2. License Grants and Conditions

2.1. Grants

Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:

  1. under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and

  2. under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.

2.2. Effective Date

The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.

2.3. Limitations on Grant Scope

The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:

  1. for any code that a Contributor has removed from Covered Software; or

  2. for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or

  3. under Patent Claims infringed by Covered Software in the absence of its Contributions.

This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).

2.4. Subsequent Licenses

No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).

2.5. Representation

Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.

2.7. Conditions

Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.

3. Responsibilities

3.1. Distribution of Source Form

All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form.

3.2. Distribution of Executable Form

If You distribute Covered Software in Executable Form then:

  1. such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and

  2. You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).

3.4. Notices

You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.

4. Inability to Comply Due to Statute or Regulation

If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.

5. Termination

5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.

6. Disclaimer of Warranty

Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.

7. Limitation of Liability

Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.

8. Litigation

Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims.

9. Miscellaneous

This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.

10. Versions of the License

10.1. New Versions

Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.

10.2. Effect of New Versions

You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.

10.3. Modified Versions

If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses

If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - “Incompatible With Secondary Licenses” Notice

This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.

================================================ FILE: Procfile ================================================ web: node hub/server.js ================================================ FILE: README.md ================================================ TogetherJS - Surprisingly easy collaboration ============================================ What is TogetherJS? ----------------- TogetherJS is a service for your website that makes it surprisingly easy to collaborate in real-time. Using TogetherJS two people can interact on the same page, seeing each other's cursors, edits, and browsing a site together. The TogetherJS service is included by the web site owner, and a web site can customize and configure aspects of TogetherJS's behavior on the site. For more information and to see TogetherJS in action, visit [togetherjs.com](https://togetherjs.com/) If you want to integrate TogetherJS onto your site see [the wiki](https://github.com/mozilla/togetherjs/wiki) and specifically [Getting Started](https://github.com/mozilla/togetherjs/wiki/Developers:-Getting-Started). Contributing ============ The remainder of this document is about contributing to TogetherJS - but reports, fixes, features, etc. Look back at those other links if you are looking for something else. Bug Reports ----------- Please submit bug reports as [github issues](https://github.com/mozilla/togetherjs/issues/new). Don't worry about labels or milestones. If you use the in-app feedback to give us a bug report that's fine too. Roadmap & Plans --------------- To see what we're planning or at least considering to do with TogetherJS, look at [see our bug tracker](https://github.com/mozilla/togetherjs/issues?state=open). Setting up a development environment ------------------------------------ TogetherJS has two main pieces: * The [server](https://github.com/mozilla/togetherjs/blob/develop/hub/server.js), which echos messages back and forth between users. The server doesn't do much, you may gaze upon its incredibly boring [history](https://github.com/mozilla/togetherjs/commits/develop/hub/server.js). * The client in [`togetherjs/`](https://github.com/mozilla/togetherjs/tree/develop/togetherjs) which does all the real work. There is a TogetherJS hub server deployed at `https://hub.togetherjs.com` - and there's little need for other server deployments. If you want to try TogetherJS out we recommend you use our hub server. Note if you include TogetherJS on an https site, you must use an https hub server. The files need to be lightly "built": we use [LESS](http://lesscss.org/) for styles, and a couple files are generated. To develop you need to build the library using [Grunt](http://gruntjs.com/). To build a copy of the library, check out TogetherJS: ```sh $ git clone git://github.com/mozilla/togetherjs.git $ cd togetherjs ``` Then [install npm](http://nodejs.org/download/) and run: ```sh $ npm install $ npm install -g grunt-cli ``` This will install a bunch of stuff, most of which is only used for development. The only "server" dependency is [WebSocket-Node](https://github.com/Worlize/WebSocket-Node) (and if you use our hub then you don't need to worry about the server). By default everything is installed locally, i.e., in `node_modules/`. This works just fine, but it is useful to install the `grunt` command-line program globally, which `npm install -g grunt-cli` does. Now you can build TogetherJS, like: ```sh $ grunt build buildsite --no-hardlink ``` This will create a copy of the entire `togetherjs.com` site in `build/`. You'll need to setup a local web server of your own pointed to the `build/` directory. To start a server on port 8080, run: ```sh $ node devserver.js ``` If you want to develop with TogetherJS you probably want the files built continually. To do this use: ```sh $ grunt devwatch ``` This will rebuild when changes are detected. Note that Grunt is configured to create [hard links](http://en.wikipedia.org/wiki/Hard_link) instead of copying so that most changes you make to files in `togetherjs/` don't need to be rebuilt to show up in `build/togetherjs/`. `--no-hardlink` turns this behavior off. You may wish to create a static copy of the TogetherJS client to distribute and use on your website. To do this run: ```sh $ grunt build --base-url https://myapp.com --no-hardlink --dest static-myapp ``` Then `static-myapp/togetherjs.js` and `static-myapp/togetherjs-min.js` will be in place, and the rest of the code will be under `static-myapp/togetherjs/`. You would deploy these on your server. Running a local server ---------------------- You shouldn't need to run your own version of the hub server. But if you happen to make changes to the server, you can change the default hub URL by setting the HUB_URL environment variable when building. For example: ``` $ HUB_URL=http://localhost:8080 grunt devwatch ``` Testing ------- Tests are in `togetherjs/tests/` -- these are [doctest.js](http://doctestjs.org/) tests. To actually run the tests build togetherjs, serve it up, and go to `http://localhost:PORT/togetherjs/tests/` -- from there the tests are linked to from the top of the page. The actual tests are `*.js` files in `togetherjs/tests/`, generally `test_*.js` for unit-style tests, and `func_*.js` for functional tests. The "Manual testing" link is something that lets you simulate different conditions in TogetherJS without setting up a second browser/client. There is unfortunately no automated runner for these tests. It might be nice if [Karma](http://karma-runner.github.io/) could be setup with doctest.js in general, but so far that isn't done. License ------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/). ================================================ FILE: addon/.gitignore ================================================ Profile* ================================================ FILE: addon/Procfile ================================================ profilea: cfx run --binary /Applications/FirefoxNightly.app -p ProfileA profileb: sleep 1; cfx run --binary /Applications/FirefoxNightly.app -p ProfileB ================================================ FILE: addon/README.md ================================================ The Firefox Addon simply adds togetherjs.js to a window - when you turn it on the tab gets togetherjs for that one tab, and when you turn it off it stops. ================================================ FILE: addon/data/attachment.js ================================================ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ self.port.on("Config", function (config) { var doc = unsafeWindow.document; unsafeWindow._TogetherJSBookmarklet = true; unsafeWindow.TogetherJSConfig_hubBase = config.hubBase; if (config.shareId) { unsafeWindow._TogetherJSShareId = config.shareId; } var script = doc.createElement("script"); script.src = config.url; console.log("Attaching:", script.outerHTML, "to:", window.location.href); doc.head.appendChild(script); }); // FIXME: need to bind to session.on("close") and emit this: // self.port.emit("Close"); ================================================ FILE: addon/data/button.html ================================================
togetherjs
================================================ FILE: addon/data/button.js ================================================ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ self.port.on("TogetherJSOn", function () { document.getElementById("togetherjs-button").innerHTML = "truckin'"; }); self.port.on("TogetherJSOff", function () { document.getElementById("togetherjs-button").innerHTML = "togetherjs"; }); ================================================ FILE: addon/data/startup-help.html ================================================ Welcome to TogetherJS

Thanks for checking out TogetherJS.

You should see a link/button at the bottom right of the browser: togetherjs — if you don't then you need to show the Add-on Bar:

Go to View > Toolbars > Add-on bar to display the bar (or read more here).

Once you see the button, click it to start sharing your session. Once you are sharing you have to tell someone else (more instructions here)

================================================ FILE: addon/lib/main.js ================================================ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ const widgets = require("widget"); const data = require("self").data; const tabs = require("tabs"); const { StartupPanel } = require("./startup-panel"); const { Page } = require("page-worker"); const simplePrefs = require('simple-prefs'); var button = widgets.Widget({ id: "togetherjs-starter", label: "Start TogetherJS", contentURL: data.url("button.html"), contentScriptFile: data.url("button.js"), onClick: function () { console.log("Starting TogetherJS because of click"); startTogetherJS(); }, width: 48 }); StartupPanel({ name: "TogetherJS", contentURL: data.url("startup-help.html") }); var autoDomains = []; function updateAutoDomains() { var domains = simplePrefs.prefs.autoDomains; domains = domains.split(/,/g); autoDomains = []; domains.forEach(function (item) { if (item.search(/^\s*$/) === 0) { return; } item = item.replace(/^\s+/, "").replace(/\s+$/, ""); item = item.split(/;/); var domain = item[0]; var shareId = item[1] || null; if (domain.indexOf("//") == -1) { // Just a plain domain domain = "^https?:\\/\\/" + domain; } domain = new RegExp(domain, "i"); autoDomains.push({domain: domain, shareId: shareId}); }); } simplePrefs.on("autoDomains", updateAutoDomains); updateAutoDomains(); function startTogetherJS(shareId) { var tab = tabs.activeTab; if (tab.togetherjsCloser) { tab.togetherjsCloser(); return; } tab.togetherjsCloser = function () { tab.togetherjsCloser = null; button.port.emit("TogetherJSOff"); tab.removeListener("ready", attachWorker); }; var worker; function attachWorker() { worker = tab.attach({ contentScriptFile: [ data.url("attachment.js") ] }); worker.port.on("Close", function () { tab.togetherjsCloser(); }); worker.port.emit("Config", {url: simplePrefs.prefs.togetherjsJs, hubBase: simplePrefs.prefs.hubBase, shareId: shareId || null}); } button.port.emit("TogetherJSOn"); tab.on("ready", attachWorker); attachWorker(); } // Need poll for back button code tabs.on("open", watchTab); function watchTab(tab) { tab.on("ready", function () { if (tabs.activeTab == tab && tab.url.indexOf("#&togetherjs") != -1) { console.log("Starting TogetherJS on share link", tab.url); startTogetherJS(); } if (tabs.activeTab == tab && ! tab.togetherjsCloser) { var started = false; autoDomains.forEach(function (matcher) { console.log("matcher", matcher, matcher.domain); if ((! started) && tab.url.search(matcher.domain) != -1) { console.log("Start TogetherJS autoDomain"); startTogetherJS(matcher.shareId); started = true; } }); } }); } for (var i=0; i http://localhost:" + port + "/\nCTRL + C to shutdown"); ================================================ FILE: hub/server.js ================================================ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ // New Relic Server monitoring support if ( process.env.NEW_RELIC_HOME ) { require("newrelic"); } var SAMPLE_STATS_INTERVAL = 60*1000; // 1 minute var SAMPLE_LOAD_INTERVAL = 5*60*1000; // 5 minutes var EMPTY_ROOM_LOG_TIMEOUT = 3*60*1000; // 3 minutes var WEBSOCKET_COMPAT = true; var WebSocketServer = WEBSOCKET_COMPAT ? require("./websocket-compat").server : require("websocket").server; var http = require('http'); var parseUrl = require('url').parse; var fs = require('fs'); // FIXME: not sure what logger to use //var logger = require('../../lib/logger'); // LOG_LEVEL values: // 0: show everything (including debug) // 1: don't show debug, do show logger.log // 2: don't show logger.log and debug, do show logger.info (and STATS) // 3: don't show info, do show warn // 4: don't show warn, do show error // 5: don't show anything // Stats are at level 2 var thisSource = "// What follows is the source for the server.\n" + "// Obviously we can't prove this is the actual source, but if it isn't then we're \n" + "// a bunch of lying liars, so at least you have us on record.\n\n" + fs.readFileSync(__filename); var Logger = function (level, filename, stdout) { this.level = level; this.filename = filename; this.stdout = !!stdout; this._open(); process.on("SIGUSR2", (function () { this._open(); }).bind(this)); }; Logger.prototype = { write: function () { if (this.stdout) { console.log.apply(console, arguments); } if (this.file) { var s = []; for (var i=0; i EMPTY_ROOM_LOG_TIMEOUT) { logStats(id, connectionStats[id]); delete connectionStats[id]; continue; } var totalClients = countClients(connectionStats[id].clients); var connections = 0; if (allConnections[id]) { connections = allConnections[id].length; } connectionStats[id].sample.push({ time: Date.now(), totalClients: totalClients, connections: connections }); } }, SAMPLE_STATS_INTERVAL); setInterval(function () { var load = getLoad(); load.time = Date.now(); logger.info("LOAD", JSON.stringify(load)); }, SAMPLE_LOAD_INTERVAL); function getLoad() { var sessions = 0; var connections = 0; var empty = 0; var solo = 0; for (var id in allConnections) { if (allConnections[id].length) { sessions++; connections += allConnections[id].length; if (allConnections[id].length == 1) { solo++; } } else { empty++; } } return { sessions: sessions, connections: connections, empty: empty, solo: solo }; } function countClients(clients) { var n = 0; for (var clientId in clients) { n++; } return n; } function logStats(id, stats) { logger.info("STATS", JSON.stringify({ id: id, created: stats.created, sample: stats.sample, totalClients: countClients(stats.clients), totalMessageChars: stats.totalMessageChars, totalMessages: stats.totalMessages, domain: stats.firstDomain || null, domainCount: countClients(stats.domains), urls: countClients(stats.urls) })); } if (require.main == module) { var ops = require('optimist') .usage("Usage: $0 [--port 8080] [--host=localhost] [--log=filename] [--log-level=N]") .describe("port", "The port to server on (default $HUB_SERVER_PORT, $PORT, $VCAP_APP_PORT, or 8080") .describe("host", "The interface to serve on (default $HUB_SERVER_HOST, $HOST, $VCAP_APP_HOST, 127.0.0.1). Use 0.0.0.0 to make it public") .describe("log-level", "The level of logging to do, from 0 (very verbose) to 5 (nothing) (default $LOG_LEVEL or 0)") .describe("log", "A file to log to (default $LOG_FILE or stdout)") .describe("stdout", "Log to both stdout and the log file"); var port = ops.argv.port || process.env.HUB_SERVER_PORT || process.env.VCAP_APP_PORT || process.env.PORT || 8080; var host = ops.argv.host || process.env.HUB_SERVER_HOST || process.env.VCAP_APP_HOST || process.env.HOST || '127.0.0.1'; var logLevel = process.env.LOG_LEVEL || 0; var logFile = process.env.LOG_FILE || ops.argv.log; var stdout = ops.argv.stdout || !logFile; if (ops.argv['log-level']) { logLevel = parseInt(ops.argv['log-level'], 10); } logger = new Logger(logLevel, logFile, stdout); if (ops.argv.h || ops.argv.help) { console.log(ops.help()); process.exit(); } else { startServer(port, host); } } exports.startServer = startServer; ================================================ FILE: hub/websocket-compat.js ================================================ /* * A hacked websocket module which retains compatibility with the old * Hixie-76 version of the standard, needed for phantom JS (and, * presumably, very old browsers). * * This file released into the public domain * by C. Scott Ananian 2014-08-26 * * Based on https://gist.github.com/toshirot/1428579 */ var events = require("events"); var util = require("util"); var WebSocketRequest = require('websocket').request; var WebSocketServer = require('websocket').server; // Copy helpers from WebSocketServer to WebSocketRequest WebSocketRequest.prototype.connections = []; WebSocketRequest.prototype.handleRequestAccepted = WebSocketServer.prototype.handleRequestAccepted; WebSocketRequest.prototype.handleConnectionClose = WebSocketServer.prototype.handleConnectionClose; WebSocketRequest.prototype.broadcastUTF = WebSocketServer.prototype.broadcastUTF; var miksagoServerFactory = require('websocket-server'); var miksagoConnection = require('../node_modules/websocket-server/lib/ws/connection'); var CompatWebSocketServer = function(options) { events.EventEmitter.call(this); // superclass constructor var self = this; var handleConnection; // node-websocket-server (hixie-75 and hixie-76 support) var miksagoServer = miksagoServerFactory.createServer(); miksagoServer.server = options.httpServer; miksagoServer.addListener('connection', function(connection) { // Add remoteAddress property connection.remoteAddress = connection._socket.remoteAddress; // We want to use "sendUTF" regardless of the server implementation connection.sendUTF = connection.send; handleConnection(connection); }); // WebSocket-Node config (modern websocket support) var wsServerConfig = { // All options *except* 'httpServer' are required when bypassing // WebSocketServer. maxReceivedFrameSize: options.maxReceivedFrameSize || 0x10000, maxReceivedMessageSize: options.maxReceivedMessageSize || 0x100000, fragmentOutgoingMessages: true, fragmentationThreshold: 0x4000, keepalive: true, keepaliveInterval: 20000, assembleFragments: true, // autoAcceptConnections is not applicable when bypassing WebSocketServer // autoAcceptConnections: false, disableNagleAlgorithm: true, closeTimeout: 5000 }; // Handle the upgrade event ourselves instead of using WebSocketServer var wsRequest={}; options.httpServer.on('upgrade', function(req, socket, head) { if (typeof req.headers['sec-websocket-version'] !== 'undefined') { // WebSocket hybi-08/-09/-10 connection (WebSocket-Node) wsRequest = new WebSocketRequest(socket, req, wsServerConfig); try { wsRequest.readHandshake(); } catch (e) { wsRequest.reject( e.httpCode ? e.httpCode : 400, e.message, e.headers ); return; } wsRequest.once('requestAccepted', function(connection) { wsRequest.handleRequestAccepted(connection); }); self.emit('request', wsRequest); } else { // WebSocket hixie-75/-76/hybi-00 connection (node-websocket-server) if (req.method === 'GET' && (req.headers.upgrade && req.headers.connection) && req.headers.upgrade.toLowerCase() === 'websocket' && req.headers.connection.toLowerCase() === 'upgrade') { new miksagoConnection( miksagoServer.manager, miksagoServer.options, req, socket, head ); } } }); // A connection handler for old-style websockets handleConnection = function(connection) { // fake a request self.emit('request', new CompatRequest(self, connection)); }; }; util.inherits(CompatWebSocketServer, events.EventEmitter); var CompatRequest = function(server, connection) { this._server = server; this._connection = connection; this.origin = connection._options.origin || '*'; this.httpRequest = connection._req; // create wrapper right away in order to install event handlers promptly this._connectionWrapper = new CompatConnection(server, connection); }; CompatRequest.prototype.reject = function(code, message) { this._connection.reject(message || "no reason"); }; CompatRequest.prototype.accept = function(proto, origin) { // this is faked: we've already accepted the connection return this._connectionWrapper; }; var CompatConnection = function(server, connection) { var self = this; events.EventEmitter.call(this); // superclass constructor this._server = server; this._connection = connection; this.remoteAddress = connection.remoteAddress; connection.addListener('message', function(wsMessage) { // make the argument compatible with WebSocket-Node self.emit('message', { type: 'utf8', utf8Data: wsMessage }); }); connection.addListener('close', function() { self.emit('close'); }); }; util.inherits(CompatConnection, events.EventEmitter); CompatConnection.prototype.sendUTF = function(message) { return this._connection.sendUTF(message); }; module.exports.server = CompatWebSocketServer; ================================================ FILE: package.json ================================================ { "name": "togetherjs", "version": "0.4.0a", "main": "hub/server.js", "description": "Collaborative help system", "keywords": [], "repository": { "type": "git", "url": "https://github.com/mozilla/togetherjs.git" }, "dependencies": { "ejs": "~2.5.5", "ejs-locals": "~1.0.2", "express": "~3.0.6", "grunt-amd-check": "~0.5.1", "habitat": "~0.4.0", "less": "~1.3.1", "less-middleware": "~0.1.9", "newrelic": "0.9.20", "node-static": "~0.6.5", "optimist": "~0.6.0", "universal-analytics": "~0.1.3", "websocket": "~1.0.7", "websocket-server": "github:miksago/node-websocket-server#master", "winston": "~0.6.2" }, "devDependencies": { "grunt-contrib-less": "~0.5.1", "grunt-contrib-csslint": "~0.1.2", "grunt-contrib-jshint": "~0.4.3", "grunt-contrib-requirejs": "~0.4.1", "grunt-contrib-watch": "~0.4.3", "grunt": "~0.4.1", "grunt-contrib-copy": "~0.4.1", "grunt-http-server": "~0.0.5", "nunjucks": "~0.1.8a", "marked": "~0.3.4", "docco": "~0.6.2", "highlight.js": "~7.3.0", "optimist": "~0.6.0", "freeport": "~1.0.3", "grunt-lib-phantomjs": "~0.6.0" }, "engines": { "node": "~0.12.7", "npm": "^2.11.3" }, "scripts": { "start": "node hub/server.js", "test": "grunt test" } } ================================================ FILE: phantomjs/bridge.js ================================================ (function (doctest) { 'use strict'; // Function.bind is not defined in phantomjs (!) so polyfill it if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError("can't bind"); } 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; }; } // default phantomjs background is transparent document.body.bgColor = 'white'; // Send messages to the parent PhantomJS process via alert! Good times!! function sendMessage() { var args = [].slice.call(arguments); alert(JSON.stringify(args)); } // doctestjs-specific stuff. First, be sure we don't autostart: document.body.className = document.body.className.replace(/autodoctest/,''); // Now define a custom reporter which will pass the results up to grunt var PhantomReporter = function(runner) { this.runner = runner; }; PhantomReporter.prototype.logSuccess = function(example, got) { this._send('doctestjs.pass', example, got); }; PhantomReporter.prototype.logFailure = function(example, got) { this._send('doctestjs.fail', example, got); }; PhantomReporter.prototype._send = function(msg, example, got) { sendMessage(msg, { example: { expr: example.expr, summary: example.textSummary(), expected: example.expected }, got: got }); }; // Start/finish the doctest runner. window.doctestReporterHook = { finish: function() { sendMessage('doctestjs.end'); } }; window.addEventListener('load', function() { var runner = new doctest.Runner({ Reporter: PhantomReporter }); var parser = new doctest.HTMLParser(runner); parser.loadRemotes(function() { runner.init(); parser.parse(); sendMessage('doctestjs.start'); runner.run(); }); }); })(window.doctest); ================================================ FILE: site/base.tmpl ================================================ {% block title %} Mozilla Labs : TogetherJS {% endblock %} {% block configs %}{% endblock %} {% block styles %}{% endblock %} {% block body %}{% endblock %}
================================================ FILE: site/bookmarklet.html ================================================ {% extends "base.tmpl" %} {% block title %}TogetherJS Bookmarklet{% endblock %} {% block styles %} {% endblock %} {% block body %}

TogetherJS Bookmarklet

To use this bookmarklet, drag the button below to your bookmark toolbar:

Start TogetherJS

{% endblock %} ================================================ FILE: site/css/bootstrap.css ================================================ /*! * Bootstrap v3.0.0 * * Copyright 2013 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world by @mdo and @fat. */ /*! normalize.css v2.1.0 | MIT License | git.io/normalize */ article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } audio, canvas, video { display: inline-block; } audio:not([controls]) { display: none; height: 0; } [hidden] { display: none; } html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } a:focus { outline: thin dotted; } a:active, a:hover { outline: 0; } h1 { margin: 0.67em 0; font-size: 2em; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } hr { height: 0; -moz-box-sizing: content-box; box-sizing: content-box; } mark { color: #000; background: #ff0; } code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } pre { white-space: pre-wrap; } q { quotes: "\201C" "\201D" "\2018" "\2019"; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 0; } fieldset { padding: 0.35em 0.625em 0.75em; margin: 0 2px; border: 1px solid #c0c0c0; } legend { padding: 0; border: 0; } button, input, select, textarea { margin: 0; font-family: inherit; font-size: 100%; } button, input { line-height: normal; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } button[disabled], html input[disabled] { cursor: default; } input[type="checkbox"], input[type="radio"] { padding: 0; box-sizing: border-box; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } textarea { overflow: auto; vertical-align: top; } table { border-collapse: collapse; border-spacing: 0; } @media print { * { color: #000 !important; text-shadow: none !important; background: transparent !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 2cm .5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } .navbar { display: none; } .table td, .table th { background-color: #fff !important; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 62.5%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.428571429; color: #333333; background-color: #ffffff; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } button, input, select[multiple], textarea { background-image: none; } a { color: #428bca; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } img { vertical-align: middle; } .img-responsive { display: block; height: auto; max-width: 100%; } .img-rounded { border-radius: 6px; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eeeeee; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16.099999999999998px; font-weight: 200; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small { font-size: 85%; } cite { font-style: normal; } .text-muted { color: #999999; } .text-primary { color: #428bca; } .text-warning { color: #c09853; } .text-danger { color: #b94a48; } .text-success { color: #468847; } .text-info { color: #3a87ad; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 500; line-height: 1.1; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small { font-weight: normal; line-height: 1; color: #999999; } h1, h2, h3 { margin-top: 20px; margin-bottom: 10px; } h4, h5, h6 { margin-top: 10px; margin-bottom: 10px; } h1, .h1 { font-size: 38px; } h2, .h2 { font-size: 32px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } h1 small, .h1 small { font-size: 24px; } h2 small, .h2 small { font-size: 18px; } h3 small, .h3 small, h4 small, .h4 small { font-size: 14px; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eeeeee; } ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline > li { display: inline-block; padding-right: 5px; padding-left: 5px; } dl { margin-bottom: 20px; } dt, dd { line-height: 1.428571429; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } .dl-horizontal dd:before, .dl-horizontal dd:after { display: table; content: " "; } .dl-horizontal dd:after { clear: both; } .dl-horizontal dd:before, .dl-horizontal dd:after { display: table; content: " "; } .dl-horizontal dd:after { clear: both; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #999999; } abbr.initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; border-left: 5px solid #eeeeee; } blockquote p { font-size: 17.5px; font-weight: 300; line-height: 1.25; } blockquote p:last-child { margin-bottom: 0; } blockquote small { display: block; line-height: 1.428571429; color: #999999; } blockquote small:before { content: '\2014 \00A0'; } blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; } blockquote.pull-right p, blockquote.pull-right small { text-align: right; } blockquote.pull-right small:before { content: ''; } blockquote.pull-right small:after { content: '\00A0 \2014'; } q:before, q:after, blockquote:before, blockquote:after { content: ""; } address { display: block; margin-bottom: 20px; font-style: normal; line-height: 1.428571429; } code, pre { font-family: Monaco, Menlo, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; white-space: nowrap; background-color: #f9f2f4; border-radius: 4px; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.428571429; color: #333333; word-break: break-all; word-wrap: break-word; background-color: #f5f5f5; border: 1px solid #cccccc; border-radius: 4px; } pre.prettyprint { margin-bottom: 20px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { margin-right: auto; margin-left: auto; } .container:before, .container:after { display: table; content: " "; } .container:after { clear: both; } .container:before, .container:after { display: table; content: " "; } .container:after { clear: both; } .row:before, .row:after { display: table; content: " "; } .row:after { clear: both; } .row:before, .row:after { display: table; content: " "; } .row:after { clear: both; } @media (min-width: 768px) { .container .row { margin-right: -15px; margin-left: -15px; } } .row .row { margin-right: -15px; margin-left: -15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { position: relative; min-height: 1px; padding-right: 15px; padding-left: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11 { float: left; } .col-xs-1 { width: 8.333333333333332%; } .col-xs-2 { width: 16.666666666666664%; } .col-xs-3 { width: 25%; } .col-xs-4 { width: 33.33333333333333%; } .col-xs-5 { width: 41.66666666666667%; } .col-xs-6 { width: 50%; } .col-xs-7 { width: 58.333333333333336%; } .col-xs-8 { width: 66.66666666666666%; } .col-xs-9 { width: 75%; } .col-xs-10 { width: 83.33333333333334%; } .col-xs-11 { width: 91.66666666666666%; } .col-xs-12 { width: 100%; } @media (min-width: 768px) { .container { max-width: 720px; } .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11 { float: left; } .col-sm-1 { width: 8.333333333333332%; } .col-sm-2 { width: 16.666666666666664%; } .col-sm-3 { width: 25%; } .col-sm-4 { width: 33.33333333333333%; } .col-sm-5 { width: 41.66666666666667%; } .col-sm-6 { width: 50%; } .col-sm-7 { width: 58.333333333333336%; } .col-sm-8 { width: 66.66666666666666%; } .col-sm-9 { width: 75%; } .col-sm-10 { width: 83.33333333333334%; } .col-sm-11 { width: 91.66666666666666%; } .col-sm-12 { width: 100%; } .col-sm-push-1 { left: 8.333333333333332%; } .col-sm-push-2 { left: 16.666666666666664%; } .col-sm-push-3 { left: 25%; } .col-sm-push-4 { left: 33.33333333333333%; } .col-sm-push-5 { left: 41.66666666666667%; } .col-sm-push-6 { left: 50%; } .col-sm-push-7 { left: 58.333333333333336%; } .col-sm-push-8 { left: 66.66666666666666%; } .col-sm-push-9 { left: 75%; } .col-sm-push-10 { left: 83.33333333333334%; } .col-sm-push-11 { left: 91.66666666666666%; } .col-sm-pull-1 { right: 8.333333333333332%; } .col-sm-pull-2 { right: 16.666666666666664%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-4 { right: 33.33333333333333%; } .col-sm-pull-5 { right: 41.66666666666667%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-7 { right: 58.333333333333336%; } .col-sm-pull-8 { right: 66.66666666666666%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-10 { right: 83.33333333333334%; } .col-sm-pull-11 { right: 91.66666666666666%; } .col-sm-offset-1 { margin-left: 8.333333333333332%; } .col-sm-offset-2 { margin-left: 16.666666666666664%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-4 { margin-left: 33.33333333333333%; } .col-sm-offset-5 { margin-left: 41.66666666666667%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-7 { margin-left: 58.333333333333336%; } .col-sm-offset-8 { margin-left: 66.66666666666666%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-10 { margin-left: 83.33333333333334%; } .col-sm-offset-11 { margin-left: 91.66666666666666%; } } @media (min-width: 992px) { .container { max-width: 940px; } .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11 { float: left; } .col-md-1 { width: 8.333333333333332%; } .col-md-2 { width: 16.666666666666664%; } .col-md-3 { width: 25%; } .col-md-4 { width: 33.33333333333333%; } .col-md-5 { width: 41.66666666666667%; } .col-md-6 { width: 50%; } .col-md-7 { width: 58.333333333333336%; } .col-md-8 { width: 66.66666666666666%; } .col-md-9 { width: 75%; } .col-md-10 { width: 83.33333333333334%; } .col-md-11 { width: 91.66666666666666%; } .col-md-12 { width: 100%; } .col-md-push-0 { left: auto; } .col-md-push-1 { left: 8.333333333333332%; } .col-md-push-2 { left: 16.666666666666664%; } .col-md-push-3 { left: 25%; } .col-md-push-4 { left: 33.33333333333333%; } .col-md-push-5 { left: 41.66666666666667%; } .col-md-push-6 { left: 50%; } .col-md-push-7 { left: 58.333333333333336%; } .col-md-push-8 { left: 66.66666666666666%; } .col-md-push-9 { left: 75%; } .col-md-push-10 { left: 83.33333333333334%; } .col-md-push-11 { left: 91.66666666666666%; } .col-md-pull-0 { right: auto; } .col-md-pull-1 { right: 8.333333333333332%; } .col-md-pull-2 { right: 16.666666666666664%; } .col-md-pull-3 { right: 25%; } .col-md-pull-4 { right: 33.33333333333333%; } .col-md-pull-5 { right: 41.66666666666667%; } .col-md-pull-6 { right: 50%; } .col-md-pull-7 { right: 58.333333333333336%; } .col-md-pull-8 { right: 66.66666666666666%; } .col-md-pull-9 { right: 75%; } .col-md-pull-10 { right: 83.33333333333334%; } .col-md-pull-11 { right: 91.66666666666666%; } .col-md-offset-0 { margin-left: 0; } .col-md-offset-1 { margin-left: 8.333333333333332%; } .col-md-offset-2 { margin-left: 16.666666666666664%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-4 { margin-left: 33.33333333333333%; } .col-md-offset-5 { margin-left: 41.66666666666667%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-7 { margin-left: 58.333333333333336%; } .col-md-offset-8 { margin-left: 66.66666666666666%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-10 { margin-left: 83.33333333333334%; } .col-md-offset-11 { margin-left: 91.66666666666666%; } } @media (min-width: 1200px) { .container { max-width: 1140px; } .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11 { float: left; } .col-lg-1 { width: 8.333333333333332%; } .col-lg-2 { width: 16.666666666666664%; } .col-lg-3 { width: 25%; } .col-lg-4 { width: 33.33333333333333%; } .col-lg-5 { width: 41.66666666666667%; } .col-lg-6 { width: 50%; } .col-lg-7 { width: 58.333333333333336%; } .col-lg-8 { width: 66.66666666666666%; } .col-lg-9 { width: 75%; } .col-lg-10 { width: 83.33333333333334%; } .col-lg-11 { width: 91.66666666666666%; } .col-lg-12 { width: 100%; } .col-lg-push-0 { left: auto; } .col-lg-push-1 { left: 8.333333333333332%; } .col-lg-push-2 { left: 16.666666666666664%; } .col-lg-push-3 { left: 25%; } .col-lg-push-4 { left: 33.33333333333333%; } .col-lg-push-5 { left: 41.66666666666667%; } .col-lg-push-6 { left: 50%; } .col-lg-push-7 { left: 58.333333333333336%; } .col-lg-push-8 { left: 66.66666666666666%; } .col-lg-push-9 { left: 75%; } .col-lg-push-10 { left: 83.33333333333334%; } .col-lg-push-11 { left: 91.66666666666666%; } .col-lg-pull-0 { right: auto; } .col-lg-pull-1 { right: 8.333333333333332%; } .col-lg-pull-2 { right: 16.666666666666664%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-4 { right: 33.33333333333333%; } .col-lg-pull-5 { right: 41.66666666666667%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-7 { right: 58.333333333333336%; } .col-lg-pull-8 { right: 66.66666666666666%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-10 { right: 83.33333333333334%; } .col-lg-pull-11 { right: 91.66666666666666%; } .col-lg-offset-0 { margin-left: 0; } .col-lg-offset-1 { margin-left: 8.333333333333332%; } .col-lg-offset-2 { margin-left: 16.666666666666664%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-4 { margin-left: 33.33333333333333%; } .col-lg-offset-5 { margin-left: 41.66666666666667%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-7 { margin-left: 58.333333333333336%; } .col-lg-offset-8 { margin-left: 66.66666666666666%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-10 { margin-left: 83.33333333333334%; } .col-lg-offset-11 { margin-left: 91.66666666666666%; } } table { max-width: 100%; background-color: transparent; } th { text-align: left; } .table { width: 100%; margin-bottom: 20px; } .table thead > tr > th, .table tbody > tr > th, .table tfoot > tr > th, .table thead > tr > td, .table tbody > tr > td, .table tfoot > tr > td { padding: 8px; line-height: 1.428571429; vertical-align: top; border-top: 1px solid #dddddd; } .table thead > tr > th { vertical-align: bottom; } .table caption + thead tr:first-child th, .table colgroup + thead tr:first-child th, .table thead:first-child tr:first-child th, .table caption + thead tr:first-child td, .table colgroup + thead tr:first-child td, .table thead:first-child tr:first-child td { border-top: 0; } .table tbody + tbody { border-top: 2px solid #dddddd; } .table .table { background-color: #ffffff; } .table-condensed thead > tr > th, .table-condensed tbody > tr > th, .table-condensed tfoot > tr > th, .table-condensed thead > tr > td, .table-condensed tbody > tr > td, .table-condensed tfoot > tr > td { padding: 5px; } .table-bordered { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #dddddd; } .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th { background-color: #f9f9f9; } .table-hover > tbody > tr:hover > td, .table-hover > tbody > tr:hover > th { background-color: #f5f5f5; } table col[class^="col-"] { display: table-column; float: none; } table td[class^="col-"], table th[class^="col-"] { display: table-cell; float: none; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #f5f5f5; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #dff0d8; border-color: #d6e9c6; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td { background-color: #d0e9c6; border-color: #c9e2b3; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f2dede; border-color: #eed3d7; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td { background-color: #ebcccc; border-color: #e6c1c7; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #fcf8e3; border-color: #fbeed5; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td { background-color: #faf2cc; border-color: #f8e5be; } fieldset { padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; /* IE8-9 */ line-height: normal; } input[type="file"] { display: block; } select[multiple], select[size] { height: auto; } select optgroup { font-family: inherit; font-size: inherit; font-style: inherit; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button { height: auto; } .form-control:-moz-placeholder { color: #999999; } .form-control::-moz-placeholder { color: #999999; } .form-control:-ms-input-placeholder { color: #999999; } .form-control::-webkit-input-placeholder { color: #999999; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.428571429; color: #555555; vertical-align: middle; background-color: #ffffff; border: 1px solid #cccccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eeeeee; } textarea.form-control { height: auto; } .form-group { margin-bottom: 15px; } .radio, .checkbox { display: block; min-height: 20px; padding-left: 20px; margin-top: 10px; margin-bottom: 10px; vertical-align: middle; } .radio label, .checkbox label { display: inline; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { float: left; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { display: inline-block; padding-left: 20px; margin-bottom: 0; font-weight: normal; vertical-align: middle; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], .radio[disabled], .radio-inline[disabled], .checkbox[disabled], .checkbox-inline[disabled], fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"], fieldset[disabled] .radio, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .input-sm { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm { height: auto; } .input-lg { height: 45px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-lg { height: 45px; line-height: 45px; } textarea.input-lg { height: auto; } .has-warning .help-block, .has-warning .control-label { color: #c09853; } .has-warning .form-control { border-color: #c09853; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus { border-color: #a47e3c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; } .has-warning .input-group-addon { color: #c09853; background-color: #fcf8e3; border-color: #c09853; } .has-error .help-block, .has-error .control-label { color: #b94a48; } .has-error .form-control { border-color: #b94a48; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus { border-color: #953b39; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; } .has-error .input-group-addon { color: #b94a48; background-color: #f2dede; border-color: #b94a48; } .has-success .help-block, .has-success .control-label { color: #468847; } .has-success .form-control { border-color: #468847; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus { border-color: #356635; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; } .has-success .input-group-addon { color: #468847; background-color: #dff0d8; border-color: #468847; } .form-control-static { padding-top: 6px; margin-bottom: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; } .form-inline .radio, .form-inline .checkbox { display: inline-block; padding-left: 0; margin-top: 0; margin-bottom: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } .form-horizontal .control-label, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { padding-top: 6px; } .form-horizontal .form-group:before, .form-horizontal .form-group:after { display: table; content: " "; } .form-horizontal .form-group:after { clear: both; } .form-horizontal .form-group:before, .form-horizontal .form-group:after { display: table; content: " "; } .form-horizontal .form-group:after { clear: both; } @media (min-width: 768px) { .container .form-horizontal .form-group { margin-right: -15px; margin-left: -15px; } } .form-horizontal .form-group .row { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { text-align: right; } } .btn { display: inline-block; padding: 6px 12px; margin-bottom: 0; font-size: 14px; font-weight: bold; line-height: 1.428571429; text-align: center; white-space: nowrap; vertical-align: middle; cursor: pointer; border: 1px solid transparent; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; } .btn:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus { color: #333333; text-decoration: none; } .btn:active, .btn.active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { pointer-events: none; cursor: not-allowed; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; } .btn-default { color: #333333; background-color: #ffffff; border-color: #cccccc; } .btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default { color: #333333; background-color: #ebebeb; border-color: #adadad; } .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { background-color: #ffffff; border-color: #cccccc; } .btn-primary { color: #ffffff; background-color: #428bca; border-color: #357ebd; } .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { color: #ffffff; background-color: #3276b1; border-color: #285e8e; } .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #428bca; border-color: #357ebd; } .btn-warning { color: #ffffff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning { color: #ffffff; background-color: #ed9c28; border-color: #d58512; } .btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { background-color: #f0ad4e; border-color: #eea236; } .btn-danger { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger { color: #ffffff; background-color: #d2322d; border-color: #ac2925; } .btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { background-color: #d9534f; border-color: #d43f3a; } .btn-success { color: #ffffff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { color: #ffffff; background-color: #47a447; border-color: #398439; } .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { background-color: #5cb85c; border-color: #4cae4c; } .btn-info { color: #ffffff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:hover, .btn-info:focus, .btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info { color: #ffffff; background-color: #39b3d7; border-color: #269abc; } .btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { background-color: #5bc0de; border-color: #46b8da; } .btn-link { font-weight: normal; color: #428bca; cursor: pointer; border-radius: 0; } .btn-link, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #2a6496; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #999999; text-decoration: none; } .btn-lg { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-sm, .btn-xs { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs { padding: 1px 5px; } .btn-block { display: block; width: 100%; padding-right: 0; padding-left: 0; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition: height 0.35s ease; transition: height 0.35s ease; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group.col { float: none; padding-right: 0; padding-left: 0; } .input-group .form-control { width: 100%; margin-bottom: 0; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 45px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 45px; line-height: 45px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 14px; font-weight: normal; line-height: 1; text-align: center; background-color: #eeeeee; border: 1px solid #cccccc; border-radius: 4px; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 3px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 18px; border-radius: 6px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -4px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:active { z-index: 2; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px solid #000000; border-right: 4px solid transparent; border-left: 4px solid transparent; content: ""; } .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; font-size: 14px; list-style: none; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.428571429; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { color: #ffffff; text-decoration: none; background-color: #428bca; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #ffffff; text-decoration: none; background-color: #428bca; outline: 0; } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #999999; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; cursor: not-allowed; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.428571429; color: #999999; } .dropdown-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 990; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0; border-bottom: 4px solid #000000; content: ""; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } .list-group { padding-left: 0; margin-bottom: 20px; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #ffffff; border: 1px solid #dddddd; } .list-group-item:first-child { border-top-right-radius: 4px; border-top-left-radius: 4px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } a.list-group-item { color: #555555; } a.list-group-item .list-group-item-heading { color: #333333; } a.list-group-item:hover, a.list-group-item:focus { text-decoration: none; background-color: #f5f5f5; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #ffffff; background-color: #428bca; border-color: #428bca; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading { color: inherit; } .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { color: #e1edf7; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 20px; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } .panel-body { padding: 15px; } .panel > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item { border-width: 1px 0; } .panel > .list-group .list-group-item:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .panel > .list-group .list-group-item:last-child { border-bottom: 0; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .panel-heading { padding: 10px 15px; background-color: #f5f5f5; border-bottom: 1px solid #dddddd; border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 17.5px; } .panel-title > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #dddddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel-group .panel { margin-bottom: 0; overflow: hidden; border-radius: 4px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse .panel-body { border-top: 1px solid #dddddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #dddddd; } .panel-primary { border-color: #428bca; } .panel-primary > .panel-heading { color: #ffffff; background-color: #428bca; border-color: #428bca; } .panel-primary > .panel-heading + .panel-collapse .panel-body { border-top-color: #428bca; } .panel-primary > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #428bca; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #468847; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #d6e9c6; } .panel-warning { border-color: #fbeed5; } .panel-warning > .panel-heading { color: #c09853; background-color: #fcf8e3; border-color: #fbeed5; } .panel-warning > .panel-heading + .panel-collapse .panel-body { border-top-color: #fbeed5; } .panel-warning > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #fbeed5; } .panel-danger { border-color: #eed3d7; } .panel-danger > .panel-heading { color: #b94a48; background-color: #f2dede; border-color: #eed3d7; } .panel-danger > .panel-heading + .panel-collapse .panel-body { border-top-color: #eed3d7; } .panel-danger > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #eed3d7; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #3a87ad; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #bce8f1; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-lg { padding: 24px; border-radius: 6px; } .well-sm { padding: 9px; border-radius: 3px; } .close { float: right; font-size: 21px; font-weight: bold; line-height: 1; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover, .close:focus { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.5; filter: alpha(opacity=50); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .nav { padding-left: 0; margin-bottom: 0; list-style: none; } .nav:before, .nav:after { display: table; content: " "; } .nav:after { clear: both; } .nav:before, .nav:after { display: table; content: " "; } .nav:after { clear: both; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eeeeee; } .nav > li.disabled > a { color: #999999; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #999999; text-decoration: none; cursor: not-allowed; background-color: transparent; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eeeeee; border-color: #428bca; } .nav .nav-divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #dddddd; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.428571429; border: 1px solid transparent; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #dddddd; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555555; cursor: default; background-color: #ffffff; border: 1px solid #dddddd; border-bottom-color: transparent; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { display: table-cell; float: none; width: 1%; } .nav-tabs.nav-justified > li > a { text-align: center; } .nav-tabs.nav-justified > li > a { margin-right: 0; border-bottom: 1px solid #dddddd; } .nav-tabs.nav-justified > .active > a { border-bottom-color: #ffffff; } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 5px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #ffffff; background-color: #428bca; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { display: table-cell; float: none; width: 1%; } .nav-justified > li > a { text-align: center; } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-bottom: 1px solid #dddddd; } .nav-tabs-justified > .active > a { border-bottom-color: #ffffff; } .tabbable:before, .tabbable:after { display: table; content: " "; } .tabbable:after { clear: both; } .tabbable:before, .tabbable:after { display: table; content: " "; } .tabbable:after { clear: both; } .tab-content > .tab-pane, .pill-content > .pill-pane { display: none; } .tab-content > .active, .pill-content > .active { display: block; } .nav .caret { border-top-color: #428bca; border-bottom-color: #428bca; } .nav a:hover .caret { border-top-color: #2a6496; border-bottom-color: #2a6496; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 20px; background-color: #f8f8f8; border: 1px solid #e7e7e7; } .navbar:before, .navbar:after { display: table; content: " "; } .navbar:after { clear: both; } .navbar:before, .navbar:after { display: table; content: " "; } .navbar:after { clear: both; } @media (min-width: 768px) { .navbar { border-radius: 4px; } } .navbar-header { padding-right: 15px; padding-left: 15px; } .navbar-header:before, .navbar-header:after { display: table; content: " "; } .navbar-header:after { clear: both; } .navbar-header:before, .navbar-header:after { display: table; content: " "; } .navbar-header:after { clear: both; } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { max-height: 340px; padding-right: 15px; padding-left: 15px; overflow-x: visible; overflow-y: auto; border-top: 1px solid #e6e6e6; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -webkit-overflow-scrolling: touch; } .navbar-collapse:before, .navbar-collapse:after { display: table; content: " "; } .navbar-collapse:after { clear: both; } .navbar-collapse:before, .navbar-collapse:after { display: table; content: " "; } .navbar-collapse:after { clear: both; } @media (min-width: 768px) { .navbar-collapse { width: auto; padding-right: 0; padding-left: 0; border-top: 0; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } } .navbar-static-top { border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; } .navbar-brand { float: left; padding-top: 15px; padding-bottom: 15px; font-size: 18px; line-height: 20px; color: #777777; } .navbar-brand:hover, .navbar-brand:focus { color: #5e5e5e; text-decoration: none; background-color: transparent; } .navbar-toggle { position: relative; float: right; padding: 9px 10px; margin-top: 8px; margin-bottom: 8px; background-color: transparent; border: 1px solid #dddddd; border-radius: 4px; } .navbar-toggle:hover, .navbar-toggle:focus { background-color: #dddddd; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; background-color: #cccccc; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { position: relative; top: auto; left: auto; display: none; } } .navbar-nav { padding-top: 7.5px; padding-bottom: 7.5px; margin-right: -15px; margin-left: -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 20px; color: #777777; } .navbar-nav > li > a:hover, .navbar-nav > li > a:focus { color: #333333; background-color: transparent; } .navbar-nav > .active > a, .navbar-nav > .active > a:hover, .navbar-nav > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-nav > .disabled > a, .navbar-nav > .disabled > a:hover, .navbar-nav > .disabled > a:focus { color: #cccccc; background-color: transparent; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 20px; color: #777777; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { color: #333333; background-color: transparent; background-image: none; } .navbar-nav .open .dropdown-menu > .active > a, .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #cccccc; background-color: transparent; } } @media (min-width: 768px) { .navbar-nav { float: left; padding-top: 0; padding-bottom: 0; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 15px; padding-bottom: 15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; } .navbar-right .dropdown-menu { right: 0; left: auto; } } .navbar-form { padding: 10px 15px; margin-top: 8px; margin-right: -15px; margin-bottom: 8px; margin-left: -15px; border-top: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; padding-left: 0; margin-top: 0; margin-bottom: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } } @media (min-width: 768px) { .navbar-form { width: auto; padding-top: 0; padding-bottom: 0; margin-right: 0; margin-left: 0; border: 0; -webkit-box-shadow: none; box-shadow: none; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-nav > .dropdown > a:hover .caret, .navbar-nav > .dropdown > a:focus .caret { border-top-color: #333333; border-bottom-color: #333333; } .navbar-nav > .open > a, .navbar-nav > .open > a:hover, .navbar-nav > .open > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-nav > .open > a .caret, .navbar-nav > .open > a:hover .caret, .navbar-nav > .open > a:focus .caret { border-top-color: #555555; border-bottom-color: #555555; } .navbar-nav > .dropdown > a .caret { border-top-color: #777777; border-bottom-color: #777777; } .navbar-nav.pull-right > li > .dropdown-menu, .navbar-nav > li > .dropdown-menu.pull-right { right: 0; left: auto; } .navbar-btn { margin-top: 8px; margin-bottom: 8px; } .navbar-text { float: left; margin-top: 15px; margin-bottom: 15px; color: #777777; } @media (min-width: 768px) { .navbar-text { margin-right: 15px; margin-left: 15px; } } .navbar-link { color: #777777; } .navbar-link:hover { color: #333333; } .navbar-inverse { background-color: #222222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #999999; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-text { color: #999999; } .navbar-inverse .navbar-nav > li > a { color: #999999; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #ffffff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .dropdown > a:hover .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } .navbar-inverse .navbar-nav > .dropdown > a .caret { border-top-color: #999999; border-bottom-color: #999999; } .navbar-inverse .navbar-nav > .open > a .caret, .navbar-inverse .navbar-nav > .open > a:hover .caret, .navbar-inverse .navbar-nav > .open > a:focus .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #999999; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #999999; } .navbar-inverse .navbar-link:hover { color: #ffffff; } .btn-default .caret { border-top-color: #333333; } .btn-primary .caret, .btn-success .caret, .btn-warning .caret, .btn-danger .caret, .btn-info .caret { border-top-color: #fff; } .dropup .btn-default .caret { border-bottom-color: #333333; } .dropup .btn-primary .caret, .dropup .btn-success .caret, .dropup .btn-warning .caret, .dropup .btn-danger .caret, .dropup .btn-info .caret { border-bottom-color: #fff; } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group > .btn:focus, .btn-group-vertical > .btn:focus { outline: none; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar:before, .btn-toolbar:after { display: table; content: " "; } .btn-toolbar:after { clear: both; } .btn-toolbar:before, .btn-toolbar:after { display: table; content: " "; } .btn-toolbar:after { clear: both; } .btn-toolbar .btn-group { float: left; } .btn-toolbar > .btn + .btn, .btn-toolbar > .btn-group + .btn, .btn-toolbar > .btn + .btn-group, .btn-toolbar > .btn-group + .btn-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child > .btn:last-child, .btn-group > .btn-group:first-child > .dropdown-toggle { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:last-child > .btn:first-child { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group-xs > .btn { padding: 5px 10px; padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-group-lg > .btn { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-group > .btn + .dropdown-toggle { padding-right: 8px; padding-left: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-right: 12px; padding-left: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px; } .dropup .btn-lg .caret { border-bottom-width: 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after { display: table; content: " "; } .btn-group-vertical > .btn-group:after { clear: both; } .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after { display: table; content: " "; } .btn-group-vertical > .btn-group:after { clear: both; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-right-radius: 0; border-bottom-left-radius: 4px; border-top-left-radius: 0; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child > .btn:last-child, .btn-group-vertical > .btn-group:first-child > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child > .btn:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .btn-group-justified { display: table; width: 100%; border-collapse: separate; table-layout: fixed; } .btn-group-justified .btn { display: table-cell; float: none; width: 1%; } [data-toggle="buttons"] > .btn > input[type="radio"], [data-toggle="buttons"] > .btn > input[type="checkbox"] { display: none; } .breadcrumb { padding: 8px 15px; margin-bottom: 20px; list-style: none; background-color: #f5f5f5; border-radius: 4px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { padding: 0 5px; color: #cccccc; content: "/\00a0"; } .breadcrumb > .active { color: #999999; } .pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.428571429; text-decoration: none; background-color: #ffffff; border: 1px solid #dddddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 4px; border-top-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { background-color: #eeeeee; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; color: #ffffff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination > .disabled > span, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #999999; cursor: not-allowed; background-color: #ffffff; border-color: #dddddd; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 18px; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-bottom-left-radius: 6px; border-top-left-radius: 6px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-top-right-radius: 6px; border-bottom-right-radius: 6px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .pager { padding-left: 0; margin: 20px 0; text-align: center; list-style: none; } .pager:before, .pager:after { display: table; content: " "; } .pager:after { clear: both; } .pager:before, .pager:after { display: table; content: " "; } .pager:after { clear: both; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #428bca; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #999999; cursor: not-allowed; background-color: #ffffff; } .modal-open { overflow: hidden; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; display: none; overflow: auto; overflow-y: scroll; } .modal.fade .modal-dialog { -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); transform: translate(0, -25%); -webkit-transition: -webkit-transform 0.3s ease-out; -moz-transition: -moz-transform 0.3s ease-out; -o-transition: -o-transform 0.3s ease-out; transition: transform 0.3s ease-out; } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .modal-dialog { z-index: 1050; width: auto; padding: 10px; margin-right: auto; margin-left: auto; } .modal-content { position: relative; background-color: #ffffff; border: 1px solid #999999; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; outline: none; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); background-clip: padding-box; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1030; background-color: #000000; } .modal-backdrop.fade { opacity: 0; filter: alpha(opacity=0); } .modal-backdrop.in { opacity: 0.5; filter: alpha(opacity=50); } .modal-header { min-height: 16.428571429px; padding: 15px; border-bottom: 1px solid #e5e5e5; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: 1.428571429; } .modal-body { position: relative; padding: 20px; } .modal-footer { padding: 19px 20px 20px; margin-top: 15px; text-align: right; border-top: 1px solid #e5e5e5; } .modal-footer:before, .modal-footer:after { display: table; content: " "; } .modal-footer:after { clear: both; } .modal-footer:before, .modal-footer:after { display: table; content: " "; } .modal-footer:after { clear: both; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } @media screen and (min-width: 768px) { .modal-dialog { right: auto; left: 50%; width: 600px; padding-top: 30px; padding-bottom: 30px; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); } } .tooltip { position: absolute; z-index: 1030; display: block; font-size: 12px; line-height: 1.4; opacity: 0; filter: alpha(opacity=0); visibility: visible; } .tooltip.in { opacity: 0.9; filter: alpha(opacity=90); } .tooltip.top { padding: 5px 0; margin-top: -3px; } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip.bottom { padding: 5px 0; margin-top: 3px; } .tooltip.left { padding: 0 5px; margin-left: -3px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #ffffff; text-align: center; text-decoration: none; background-color: #000000; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.top-left .tooltip-arrow { bottom: 0; left: 5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.top-right .tooltip-arrow { right: 5px; bottom: 0; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-right-color: #000000; border-width: 5px 5px 5px 0; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-left-color: #000000; border-width: 5px 0 5px 5px; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .tooltip.bottom-left .tooltip-arrow { top: 0; left: 5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .tooltip.bottom-right .tooltip-arrow { top: 0; right: 5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .popover { position: absolute; top: 0; left: 0; z-index: 1010; display: none; max-width: 276px; padding: 1px; text-align: left; white-space: normal; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); background-clip: padding-box; } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; font-weight: normal; line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; } .popover-content { padding: 9px 14px; } .popover .arrow, .popover .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover .arrow { border-width: 11px; } .popover .arrow:after { border-width: 10px; content: ""; } .popover.top .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999999; border-top-color: rgba(0, 0, 0, 0.25); border-bottom-width: 0; } .popover.top .arrow:after { bottom: 1px; margin-left: -10px; border-top-color: #ffffff; border-bottom-width: 0; content: " "; } .popover.right .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999999; border-right-color: rgba(0, 0, 0, 0.25); border-left-width: 0; } .popover.right .arrow:after { bottom: -10px; left: 1px; border-right-color: #ffffff; border-left-width: 0; content: " "; } .popover.bottom .arrow { top: -11px; left: 50%; margin-left: -11px; border-bottom-color: #999999; border-bottom-color: rgba(0, 0, 0, 0.25); border-top-width: 0; } .popover.bottom .arrow:after { top: 1px; margin-left: -10px; border-bottom-color: #ffffff; border-top-width: 0; content: " "; } .popover.left .arrow { top: 50%; right: -11px; margin-top: -11px; border-left-color: #999999; border-left-color: rgba(0, 0, 0, 0.25); border-right-width: 0; } .popover.left .arrow:after { right: 1px; bottom: -10px; border-left-color: #ffffff; border-right-width: 0; content: " "; } .alert { padding: 15px; margin-bottom: 20px; color: #c09853; background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert hr { border-top-color: #f8e5be; } .alert .alert-link { font-weight: bold; color: #a47e3c; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable { padding-right: 35px; } .alert-dismissable .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { color: #468847; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #356635; } .alert-danger { color: #b94a48; background-color: #f2dede; border-color: #eed3d7; } .alert-danger hr { border-top-color: #e6c1c7; } .alert-danger .alert-link { color: #953b39; } .alert-info { color: #3a87ad; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #2d6987; } .thumbnail, .img-thumbnail { padding: 4px; line-height: 1.428571429; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .thumbnail { display: block; } .thumbnail > img { display: block; height: auto; max-width: 100%; } .img-thumbnail { display: inline-block; height: auto; max-width: 100%; } a.thumbnail:hover, a.thumbnail:focus { border-color: #428bca; } .thumbnail > img { margin-right: auto; margin-left: auto; } .thumbnail .caption { padding: 9px; color: #333333; } .media, .media-body { overflow: hidden; zoom: 1; } .media, .media .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media-object { display: block; } .media-heading { margin: 0 0 5px; } .media > .pull-left { margin-right: 10px; } .media > .pull-right { margin-left: 10px; } .media-list { padding-left: 0; list-style: none; } .label { display: inline; padding: .25em .6em; font-size: 75%; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } .label[href]:hover, .label[href]:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .label-default { background-color: #999999; } .label-default[href]:hover, .label-default[href]:focus { background-color: #808080; } .label-primary { background-color: #428bca; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #3071a9; } .label-success { background-color: #5cb85c; } .label-success[href]:hover, .label-success[href]:focus { background-color: #449d44; } .label-info { background-color: #5bc0de; } .label-info[href]:hover, .label-info[href]:focus { background-color: #31b0d5; } .label-warning { background-color: #f0ad4e; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #ec971f; } .label-danger { background-color: #d9534f; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #c9302c; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; background-color: #999999; border-radius: 10px; } .badge:empty { display: none; } a.badge:hover, a.badge:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .btn .badge { position: relative; top: -1px; } a.list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #428bca; background-color: #ffffff; } .nav-pills > li > a > .badge { margin-left: 3px; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-moz-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 20px; margin-bottom: 20px; overflow: hidden; background-color: #f5f5f5; border-radius: 4px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress-bar { float: left; width: 0; height: 100%; font-size: 12px; color: #ffffff; text-align: center; background-color: #428bca; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-transition: width 0.6s ease; transition: width 0.6s ease; } .progress-striped .progress-bar { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-size: 40px 40px; } .progress.active .progress-bar { -webkit-animation: progress-bar-stripes 2s linear infinite; -moz-animation: progress-bar-stripes 2s linear infinite; -ms-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar-success { background-color: #5cb85c; } .progress-striped .progress-bar-success { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #5bc0de; } .progress-striped .progress-bar-info { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #f0ad4e; } .progress-striped .progress-bar-warning { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #d9534f; } .progress-striped .progress-bar-danger { background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; height: auto; max-width: 100%; line-height: 1; } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; bottom: 0; left: 0; width: 15%; font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); opacity: 0.5; filter: alpha(opacity=50); } .carousel-control.left { background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { right: 0; left: auto; background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next { position: absolute; top: 50%; left: 50%; z-index: 5; display: inline-block; width: 20px; height: 20px; margin-top: -10px; margin-left: -10px; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; padding-left: 0; margin-left: -30%; text-align: center; list-style: none; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; cursor: pointer; border: 1px solid #ffffff; border-radius: 10px; } .carousel-indicators .active { width: 12px; height: 12px; margin: 0; background-color: #ffffff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; margin-left: -15px; font-size: 30px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .jumbotron { padding: 30px; margin-bottom: 30px; font-size: 21px; font-weight: 200; line-height: 2.1428571435; color: inherit; background-color: #eeeeee; } .jumbotron h1 { line-height: 1; color: inherit; } .jumbotron p { line-height: 1.4; } @media screen and (min-width: 768px) { .jumbotron { padding: 50px 60px; border-radius: 6px; } .jumbotron h1 { font-size: 63px; } } .clearfix:before, .clearfix:after { display: table; content: " "; } .clearfix:after { clear: both; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .affix { position: fixed; } @-ms-viewport { width: device-width; } @media screen and (max-width: 400px) { @-ms-viewport { width: 320px; } } .hidden { display: none !important; visibility: hidden !important; } .visible-xs { display: block !important; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } @media (min-width: 768px) and (max-width: 991px) { .visible-xs { display: none !important; } tr.visible-xs { display: none !important; } th.visible-xs, td.visible-xs { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-xs { display: none !important; } tr.visible-xs { display: none !important; } th.visible-xs, td.visible-xs { display: none !important; } } @media (min-width: 1200px) { .visible-xs { display: none !important; } tr.visible-xs { display: none !important; } th.visible-xs, td.visible-xs { display: none !important; } } .visible-sm { display: none !important; } tr.visible-sm { display: none !important; } th.visible-sm, td.visible-sm { display: none !important; } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-sm { display: none !important; } tr.visible-sm { display: none !important; } th.visible-sm, td.visible-sm { display: none !important; } } @media (min-width: 1200px) { .visible-sm { display: none !important; } tr.visible-sm { display: none !important; } th.visible-sm, td.visible-sm { display: none !important; } } .visible-md { display: none !important; } tr.visible-md { display: none !important; } th.visible-md, td.visible-md { display: none !important; } @media (min-width: 768px) and (max-width: 991px) { .visible-md { display: none !important; } tr.visible-md { display: none !important; } th.visible-md, td.visible-md { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-md { display: none !important; } tr.visible-md { display: none !important; } th.visible-md, td.visible-md { display: none !important; } } .visible-lg { display: none !important; } tr.visible-lg { display: none !important; } th.visible-lg, td.visible-lg { display: none !important; } @media (min-width: 768px) and (max-width: 991px) { .visible-lg { display: none !important; } tr.visible-lg { display: none !important; } th.visible-lg, td.visible-lg { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-lg { display: none !important; } tr.visible-lg { display: none !important; } th.visible-lg, td.visible-lg { display: none !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } .hidden-xs { display: none !important; } tr.hidden-xs { display: none !important; } th.hidden-xs, td.hidden-xs { display: none !important; } @media (min-width: 768px) and (max-width: 991px) { .hidden-xs { display: block !important; } tr.hidden-xs { display: table-row !important; } th.hidden-xs, td.hidden-xs { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-xs { display: block !important; } tr.hidden-xs { display: table-row !important; } th.hidden-xs, td.hidden-xs { display: table-cell !important; } } @media (min-width: 1200px) { .hidden-xs { display: block !important; } tr.hidden-xs { display: table-row !important; } th.hidden-xs, td.hidden-xs { display: table-cell !important; } } .hidden-sm { display: block !important; } tr.hidden-sm { display: table-row !important; } th.hidden-sm, td.hidden-sm { display: table-cell !important; } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } tr.hidden-sm { display: none !important; } th.hidden-sm, td.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-sm { display: block !important; } tr.hidden-sm { display: table-row !important; } th.hidden-sm, td.hidden-sm { display: table-cell !important; } } @media (min-width: 1200px) { .hidden-sm { display: block !important; } tr.hidden-sm { display: table-row !important; } th.hidden-sm, td.hidden-sm { display: table-cell !important; } } .hidden-md { display: block !important; } tr.hidden-md { display: table-row !important; } th.hidden-md, td.hidden-md { display: table-cell !important; } @media (min-width: 768px) and (max-width: 991px) { .hidden-md { display: block !important; } tr.hidden-md { display: table-row !important; } th.hidden-md, td.hidden-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } tr.hidden-md { display: none !important; } th.hidden-md, td.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-md { display: block !important; } tr.hidden-md { display: table-row !important; } th.hidden-md, td.hidden-md { display: table-cell !important; } } .hidden-lg { display: block !important; } tr.hidden-lg { display: table-row !important; } th.hidden-lg, td.hidden-lg { display: table-cell !important; } @media (min-width: 768px) and (max-width: 991px) { .hidden-lg { display: block !important; } tr.hidden-lg { display: table-row !important; } th.hidden-lg, td.hidden-lg { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-lg { display: block !important; } tr.hidden-lg { display: table-row !important; } th.hidden-lg, td.hidden-lg { display: table-cell !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } tr.hidden-lg { display: none !important; } th.hidden-lg, td.hidden-lg { display: none !important; } } .visible-print { display: none !important; } tr.visible-print { display: none !important; } th.visible-print, td.visible-print { display: none !important; } @media print { .visible-print { display: block !important; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } .hidden-print { display: none !important; } tr.hidden-print { display: none !important; } th.hidden-print, td.hidden-print { display: none !important; } } ================================================ FILE: site/css/carousel.css ================================================ /* GLOBAL STYLES -------------------------------------------------- */ /* Padding below the footer and lighter body text */ body { padding-bottom: 40px; color: #5a5a5a; } /* CUSTOMIZE THE NAVBAR -------------------------------------------------- */ /* Special class on .container surrounding .navbar, used for positioning it into place. */ .navbar-wrapper { position: relative; z-index: 15; } /* CUSTOMIZE THE CAROUSEL -------------------------------------------------- */ /* Carousel base class */ .carousel { padding-top: 60px; padding-bottom: 60px; background: #ccc; /* Negative margin to pull up carousel. 90px is roughly margins and height of navbar. */ /*margin-top: -90px;*/ } /* Since positioning the image, we need to help out the caption */ .carousel-caption { z-index: 10; } /* Declare heights because of positioning of img element */ .carousel .item { height: 500px; } .carousel-inner > .item > img { position: absolute; top: 0; left: 0; min-width: 100%; height: 500px; } /* MARKETING CONTENT -------------------------------------------------- */ /* Pad the edges of the mobile views a bit */ .marketing { padding-left: 15px; padding-right: 15px; } /* Center align the text within the three columns below the carousel */ .marketing .col-lg-4 { text-align: center; margin-bottom: 20px; } .marketing h2 { font-weight: normal; } .marketing .col-lg-4 p { margin-left: 10px; margin-right: 10px; } /* Featurettes ------------------------- */ .featurette-divider { margin: 80px 0; /* Space out the Bootstrap
more */ } .featurette { padding-top: 120px; /* Vertically center images part 1: add padding above and below text. */ overflow: hidden; /* Vertically center images part 2: clear their floats. */ } .featurette-image { margin-top: -120px; /* Vertically center images part 3: negative margin up the image the same amount of the padding to center it. */ } /* Give some space on the sides of the floated elements so text doesn't run right into it. */ .featurette-image.pull-left { margin-right: 40px; } .featurette-image.pull-right { margin-left: 40px; } /* Thin out the marketing headings */ .featurette-heading { font-size: 50px; font-weight: 300; line-height: 1; letter-spacing: -1px; } /* RESPONSIVE CSS -------------------------------------------------- */ @media (min-width: 768px) { /* Remve the edge padding needed for mobile */ .marketing { padding-left: 0; padding-right: 0; } /* Navbar positioning foo */ .navbar-wrapper { margin-top: 20px; } /* The navbar becomes detached from the top, so we round the corners */ .navbar-wrapper .navbar { border-radius: 4px; } /* Bump up size of carousel content */ .carousel-caption p { margin-bottom: 20px; font-size: 21px; line-height: 1.4; } } ================================================ FILE: site/css/docco.css ================================================ /*--------------------- 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; border: none; } .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: #FFFFFF; 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; } ================================================ FILE: site/css/grid.css ================================================ /*THESE ARE GUIDELINES*/ .container { /* padding-left: 15px; padding-right: 15px;*/ } .row { margin-bottom: 20px; } .row .row { margin-top: 10px; margin-bottom: 0; } [class*="col-"] { padding-top: 15px; padding-bottom: 15px; border: 1px solid #ddd; /*background-color: rgba(0,0,0,.15);*/ border: 0px solid rgba(86,61,124,.2); } hr { margin-top: 40px; margin-bottom: 40px; } ================================================ FILE: site/css/jumbotron.css ================================================ body { padding-bottom: 20px; } /* Wrapping element */ /* Set some basic padding to keep content from hitting the edges */ .body-content { padding-left: 15px; padding-right: 15px; } /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { /* Let the jumbotron breathe */ .jumbotron { margin-top: 20px; } /* Remove padding from wrapping element since we kick in the grid classes here */ .body-content { padding: 0; } } ================================================ FILE: site/css/responsive-video.css ================================================ /*Courtesy of https://gist.github.com/jgarber/2302238*/ .flex-video { position: relative; padding-top: 25px; padding-bottom: 67.5%; height: 0; margin-bottom: 16px; overflow: hidden; } .flex-video.widescreen { padding-bottom: 57.25%; } .flex-video.vimeo { padding-top: 0; } .flex-video iframe, .flex-video object, .flex-video embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } @media only screen and (max-device-width: 800px), only screen and (device-width: 1024px) and (device-height: 600px), only screen and (width: 1280px) and (orientation: landscape), only screen and (device-width: 800px), only screen and (max-width: 767px) { .flex-video { padding-top: 0; } } ================================================ FILE: site/css/style.css ================================================ /* Color palette based on Mozilla Style guide http://www.mozilla.org/en-US/styleguide/identity/firefox/color/ Blue #0095DD Orange #FF9500 Dark Grey #3A4349 Red #DC3726 Green #49BA7C */ /* =Global ----------------------------------------------- */ @font-face { font-family: openSansRegular; src: url('../../togetherjs/fonts/OpenSans-Regular.ttf'); } @font-face { font-family: openSansLight; src: url('../../togetherjs/fonts/OpenSans-Light.ttf'); } @font-face { font-family: openSansBold; src: url('../../togetherjs/fonts/OpenSans-Bold.ttf'); } body { background-color: rgb(255, 255, 255); font: 300 16px/24px openSansLight, 'Open Sans', sans-serif; } p { color: rgb(51, 51, 51); font-family: 'Open Sans', sans-serif; font-size: 16px; line-height: 24px; } a { line-height: 1.38; font-size: 14px; font-family: openSansRegular, 'Open Sans', sans-serif; } a:hover { text-decoration: underline; } button { padding-bottom: 2px; background-color: #49BA7C; font-weight: bold; text-align: center; color: rgb(22, 22, 22); font-family: openSansLight, 'Open Sans', sans-serif; } .btn-primary { background: #49BA7C; border-color: #2aa08b; } .btn-primary:hover { background: #74C593; border-color: #34b7a0; } footer a { color: #0095DD; text-decoration: none; } footer a:hover { color: #008bb2; } .large { color: rgb(72, 72, 72); display: block; font-family: 'Open Sans Light', sans-serif; font-weight: normal; line-height: 68px; margin: 20px 0px; } h1 { color: rgb(72, 72, 72); display: block; font-family: 'Open Sans Light', sans-serif; font-weight: normal; letter-spacing: -2px; line-height: 48px; margin: 40px 0px; } h2 { color: rgb(72, 72, 72); display: block; font-family: 'Open Sans Light', sans-serif; font-weight: normal; letter-spacing: -1px; line-height: 32px; } h3 { color: rgb(72, 72, 72); display: block; font-family: 'Open Sans Light', sans-serif; font-weight: normal; letter-spacing: normal; line-height: 28px; } h4 { color: rgb(72, 72, 72); display: block; font-family: 'Open Sans Light', sans-serif; font-weight: normal; letter-spacing: normal; line-height: 20px; } h5 { color: rgb(72, 72, 72); display: block; font-family: 'Open Sans Light', sans-serif; font-weight: bold; letter-spacing: normal; } .small, small { color: rgb(51, 51, 51); display: block; font-family: 'Open Sans', sans-serif; font-size: 12px; line-height: 16px; } .form-control:focus { border: 1px solid #2aa08b; box-shadow: none; } /* =Tabzilla ----------------------------------------------- */ #tabzilla-panel, #tabzilla-panel *, #tabzilla-panel *:before, #tabzilla-panel *:after { -moz-box-sizing: content-box; box-sizing: content-box; } #tabzilla-panel a { font-size: 11px; line-height: 12px; } /* =Header ----------------------------------------------- */ .main-header { background-color: #3A4349; display: block; font-family: sans-serif; } .main-header a.main-logo { width: 48px; height: 48px; border-radius: 50%; border: 0px solid white; background: url('../images/togetherjs-logo@2x.png'); background-size: 50% 50%; background-repeat: no-repeat; background-position: center center; text-indent: -9999px; float: left; margin: 8px 0px; padding: 0; -webkit-transition: all 0.4s ease; -moz-transition: all 0.4s ease; transition: all 0.4s ease; overflow: hidden; -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } /*.main-header a.main-logo:hover, .main-header a.main-logo:active { background-color: #38c4ac; -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; }*/ .main-header .masthead-title { font-size: 14px; text-transform: uppercase; font-weight: 100; color: #fff; margin: 7px -2px 0px; letter-spacing: 1px; } /*.main-header .masthead-title:hover { color: #f1f1f1; }*/ .main-header .main-nav li a { color: rgb(255,255,255); opacity: 0.5; } .main-header .main-nav li a:hover { color: rgb(255, 255, 255); opacity: 1; } .main-header .navbar-header { padding-left: 0px; } .main-header .navbar-nav > .active > a { color: rgb(255, 255, 255); opacity: 1; background: none; } .main-header .main-nav li a.active { color: rgb(255,255,255); opacity: 1; } .navbar { border: 0; border-radius: 0; margin-bottom: 0; } .main-header .main-nav { padding: 6px 0px; } .main-header .navbar-toggle { width: 44px; background: rgb(72, 72, 72); margin-top: 13px; border: 1px solid #282a2c; } .main-header.navbar-inverse .navbar-collapse { border-color: #000; } .navbar-collapse { border-top: 1px solid #282a2c; } /* =Carousel ----------------------------------------------- */ #hero { background: url('../images/bg@2x.png') 50% 0 no-repeat fixed; background-size: 2042px 606px; margin: 0px auto; width: 100%; //max-width: 1920px; position: relative; } /* .hero { .at2x('../images/bg.png', 200px, 100px); } */ #hero-section { margin-bottom: 0px; } .hero-header-text { color: rgb(255, 255, 255); } .hero-tagline { color: rgb(255, 255, 255); margin-bottom: 20px; line-height: 24px; } #hero .container { padding-top: 20px; } #box-left { margin-top: 3%; } #video-area { cursor: pointer; } #video-area img { width: 100%; } /* =modal video ----------------------------------------------- */ #marketing-video { background: rgba(0,0,0,0.8); display: none; position: absolute; left: 0px; top: 0px; z-index: 99; width: 100%; height: 100%; margin-bottom: 200px; } #marketing-video #frame-video { padding-top: 30px; } .video-closebtn { color: rgba(255, 255, 255, 0.5); float: right; cursor: pointer; } /* =main section ----------------------------------------------- */ #main-section .header-text { margin: 40px 0px; } #main-section h3 { color: rgb(22, 22, 22); font-family: 'Open Sans Light',sans-serif; font-size: 32px; font-size-adjust: none; font-stretch: normal; font-style: normal; font-variant: normal; font-weight: 100; margin: 40px 0px 20px; } #main-section p { color: rgb(22, 22, 22); font-family: 'Open Sans Regular',sans-serif; font-size: 16px; line-height: 27px; } #main-section #text-descrip { margin: 0 15%; } #main-section h4 { margin-top: 20px; font-family: openSansBold, sans-serif; text-transform: uppercase; font-size: 12px; font-weight: 600; letter-spacing: 1px; line-height: 16px; } /* =tour section ----------------------------------------------- */ #main-section img.why-section { width: 40%; height: 40%; margin: 0 auto; } #tour h3 { margin-top: 60px; margin-bottom: 10px; } .why-connect-01 img { opacity: 0; position: relative; top: 20px; } .why-connect-02 img { opacity: 0; position: relative; top: 20px; } .why-connect-03 img { opacity: 0; position: relative; top: 20px; } #main-section #tour img.how-section { width: 100%; margin: 0 auto; } #howto-table { position: relative; } #howto-table td { border-top: 0px; } .howto-animation-01 p.smtextright { width: 60%; float: right; } .howto-animation-02 p.smtextleft { width: 60%; float: left; } .howto-animation-03 p.smtextright { width: 60%; float: right; } .how-section-imganim-01 { position: absolute; top: 46%; left: 11%; z-index: 8; width: 12%; } .how-section-imganim-02 { position: absolute; top: 26%; left: 6%; z-index: 7; width: 12%; } .how-section-imganim-03 { position: absolute; top: 40%; right: 45%; z-index: 7; width: 10%; display: none; } .how-section-btncollab { position: absolute; top: 36%; left: 20%; width: 10%; } .how-section-btncollabpressed { position: absolute; top: 36%; left: 20%; width: 10%; display: none; } .how-section-dockplacement { position: absolute; top: 34%; left: 51%; width: 30%; display: none; } .cursor-placement-01 { width: 8%; left: 78%; top: 33%; position: absolute; } .cursor-placement-04 { width: 8%; left: 69%; top: 27%; position: absolute; } .cursor-placement-02 { width: 10%; position: absolute; top: 57%; left: 32%; } .cursor-placement-03 { width: 10%; position: absolute; top: 65%; left: 44%; } /* =examples section ----------------------------------------------- */ #main-section #examples img { width: 50%; margin: 0 auto; background: #efefef; } #main-section #examples .ex-description { min-height: 50px; } /* =customers section ----------------------------------------------- */ #main-section #customers img { width: 100%; margin: 0 auto; background: #efefef; } #main-section #customers p.text-center { margin: 0px 15%; } /* =features section ----------------------------------------------- */ #main-section #features img { width: 64px; height: 64px; } /* =tryitnow section ----------------------------------------------- */ #tryitout pre { margin-top: 50px; } /* =footer section ----------------------------------------------- */ #footer a img { width: 50%; } #footer .ph-credit { color: rgba(72, 72, 72, 1); font-size: 10px; margin-top: 20px; } /* =app examples ----------------------------------------------- */ .app-example { padding: 0px; } .app-example iframe { } .app-example .top-bar { width: 100%; background: #c8c8c8; height: 30px; border-top-left-radius: 4px; border-top-right-radius: 4px; } .app-example .top-bar .red-circle { text-shadow: none; width: 30px; height: 30px; font-size: 20px; } .app-example .top-bar .yellow-circle { margin: 10px 6px; border-radius: 50px; width: 10px; height: 10px; background: #e6c646; border: none; padding: 4px; display: none; } .app-example .top-bar .yellow-circle:hover { cursor: default; display: none; } .app-example .top-bar .green-circle { margin: 10px 6px; border-radius: 50%; width: 10px; height: 10px; background: #5ccc8b; border: none; padding: 4px; display: none; } .app-example .top-bar .green-circle:hover { cursor: default; display: none; } .app-example .address-bar { padding: 14px; background: #e6e6e6; } .app-example .address-bar input:hover { cursor: default; } .app-example .modal-header { padding: 0px; } .app-example .modal-body { padding: 0px; } .app-example .modal-footer { margin-top: 0px; } .app-example .col-xs-12 { padding: 0px; height: 600px; } /* =animations ----------------------------------------------- */ .hero-header-text { position:relative; animation:moveDown 1s 1; -webkit-animation:moveDown 1s 1; /*Safari and Chrome*/ } @keyframes moveDown { from {top:-10px; opacity: 0;} to {top:0px; opacity: 1;} } @-webkit-keyframes moveDown /*Safari and Chrome*/ { from {top:-10px; opacity: 0;} to {top:0px; opacity: 1;} } .hero-tagline { position:relative; animation:moveUp 1s 1; -webkit-animation:moveUp 1s 1; /*Safari and Chrome*/ } @keyframes moveUp { from {top:20px; opacity: 0;} to {top:0px; opacity: 1;} } @-webkit-keyframes moveUp /*Safari and Chrome*/ { from {top:20px; opacity: 0;} to {top:0px; opacity: 1;} } .main-get-started { position:relative; animation:moveUp 1s 1; -webkit-animation:moveUp 1s 1; /*Safari and Chrome*/ } @keyframes moveUp { from {top:20px; opacity: 0;} to {top:0px; opacity: 1;} } @-webkit-keyframes moveUp /*Safari and Chrome*/ { from {top:20px; opacity: 0;} to {top:0px; opacity: 1;} } #video-area { position:relative; animation:moveRight 1s 1; -webkit-animation:moveRight 1s 1; /*Safari and Chrome*/ } @keyframes moveRight { from {right:-200px; opacity: 0;} to {right:0px; opacity: 1;} } @-webkit-keyframes moveRight /*Safari and Chrome*/ { from {right:-200px; opacity: 0;} to {right:0px; opacity: 1;} } /* =responsive hacks ----------------------------------------------- */ /* Extra small devices (phones, up to 480px) */ @media (max-width: 480px) { #features .media > .pull-left { margin-top: 20px; } #howto-table td { width: 50%; } .cursor-placement-01 { top: 25% !important; left: 78%; } .cursor-placement-04 { top: 19%; left: 69%; } .cursor-placement-02 { top: 62% !important; left: 42%; } .cursor-placement-03 { top: 45% !important; left: 50%; } .how-section-btncollab { top: 32% !important; } .how-section-btncollabpressed { top: 32% !important; } .how-section-dockplacement { top: 28% !important; } .how-section-imganim-01 { top: 46% !important; } .how-section-imganim-02 { top: 26% !important; } .how-section-imganim-03 { top: 40% !important; } .howto-animation-01 p.smtextright { width: auto; float: none; } .howto-animation-02 p.smtextleft { width: auto; float: none; } .howto-animation-03 p.smtextright { width: auto; float: none; } .large { font-size: 48px; line-height: 48px; letter-spacing: normal; } h1 { font-size: 32px; line-height: 32px; } h2 { font-size: 28px; line-height: 30px; } #main-section h3 { font-size: 24px; line-height: 26px; } #main-section p { font-size: 14px; line-height: 26px; } h3 { font-size: 20px; line-height: 20px; } h4 { font-size: 14px; line-height: 20px; font-weight: bold; } p { font-size: 14px; } #marketing-video #frame-video { margin: 26px; } a#tabzilla { display: none; } .how-section-imganim-01 { top: 26%; } .how-section-imganim-02 { top: 16%; } .how-section-imganim-03 { top: 20%; } .how-section-btncollab { top: 22%; left: 22%; } .how-section-btncollabpressed { top: 22%; left: 22%; } .how-section-dockplacement { top: 18%; left: 50%; } .cursor-placement-01 { top: 13%; left: 78%; } .cursor-placement-04 { top: 17%; left: 69%; } .cursor-placement-02 { top: 32%; left: 42%; } .cursor-placement-03 { top: 25%; left: 50%; } #footer .col-xs-12 { padding-top: 0px; padding-bottom: 0px; } #footer a { font-size: 11px; } #footer a img { width: 60px; height: 10px; } #main-section #examples .ex-description { min-height: 0px; } #main-section #examples .ex-title { min-height: 0px; } #tryitout pre { margin-top: -30px; } } /* Smaller devices (tablets, 480px and up) */ @media (min-width: 480px) { #features .media > .pull-left { margin-top: 10px; } #tryitout pre { margin-top: -26px; } #howto-table td { width: 50%; } .howto-animation-01 p.smtextright { width: auto; float: none; } .howto-animation-02 p.smtextleft { width: auto; float: none; } .howto-animation-03 p.smtextright { width: auto; float: none; } .large { font-size: 48px; line-height: 48px; letter-spacing: normal; } h1 { font-size: 32px; } h2 { font-size: 28px; } h3 { font-size: 24px; } h4 { font-size: 15px; font-weight: bold; } p { font-size: 14px; } #marketing-video #frame-video { margin: 26px; } a#tabzilla { display: inherit; } #footer a img { width: 100px; height: 17px; } #main-section #examples .ex-description { min-height: 0px; } #main-section img.why-section { width: 30%; height: 30%; } } /* Small devices (tablets, 768px and up) */ @media (min-width: 768px) { #features li.media { min-height: 140px; } #tryitout pre { margin-top: 50px; } .large { font-size: 72px; line-height: 100%; letter-spacing: normal; } h1 { font-size: 48px; line-height: 100%; } h2 { font-size: 32px; line-height: 100%; } h3 { font-size: 28px; line-height: 100%; } h4 { font-size: 20px; line-height: 100%; font-weight: normal; } p { font-size: 14px; line-height: 20px; } #marketing-video #frame-video { margin: 0 auto; } a#tabzilla { display: inherit; } #footer a img { width: 141px; height: 24px; } #main-section #examples .ex-description { min-height: 81px; } } /* Medium devices (desktops, 992px and up) */ @media (min-width: 992px) { #features li.media { min-height: 100px; } .howto-animation-01 p.smtextright { width: 60%; float: right; } .howto-animation-02 p.smtextleft { width: 60%; float: left; } .howto-animation-03 p.smtextright { width: 60%; float: right; } .large { font-size: 72px; line-height: 100%; letter-spacing: normal; } h1 { font-size: 48px; line-height: 100%; } h2 { font-size: 32px; line-height: 100%; } h3 { font-size: 28px; line-height: 100%; } h4 { font-size: 20px; line-height: 100%; } p { font-size: 14px; line-height: 20px; } #video-area img { margin-top: 7%; } #hero { height: 436px; } #marketing-video #frame-video { padding-top: 6px; } #main-section #examples .ex-description { min-height: 54px; } } /* Large devices (large desktops, 1200px and up) */ @media (min-width: 1200px) { #features li.media { min-height: 100px; } #video-area img { margin-top: 0%; } .large { font-size: 72px; line-height: 100%; letter-spacing: normal; } h1 { font-size: 48px; line-height: 100%; font-weight: 600; } h2 { font-size: 32px; line-height: 100%; } h3 { font-size: 28px; line-height: 100%; } h4 { font-size: 20px; line-height: 100%; } p { font-size: 14px; line-height: 20px; } #video-area img { /*margin-top: 11px;*/ } #hero { height: 440px; } #marketing-video #frame-video { padding-top: 8px; } } /*making the modal size larger for the app examples*/ @media screen and (min-width: 768px) { .app-example .modal-dialog { right: auto; left: 50%; width: 900px; padding-top: 30px; padding-bottom: 30px; } } /*@media (min-width: 1200px) { .navbox { max-width: 1140px; } } @media (min-width: 992px) { .navbox { max-width: 940px; } }*/ /*@media (min-width: 768px) { .navbox { max-width: 735px; } }*/ /* Source-specific styles */ #sourcecode h1 { margin-bottom: 20px; border-bottom: 1px solid rgba(0,0,0,0.1); padding-bottom: 17px; } #sourcecode h2 { margin-bottom: 18px; } #sourcecode h3 { margin-bottom: 18px; } #sourcecode pre { margin: 10px 0 20px; } #sourcecode #sidenav { margin-top: 40px; } #sourcecode #sidenav.panel .panel-heading { background: #fff; border-bottom: none; font-weight: 700; } #sourcecode #sidenav .affix { width: 263px; position: static; } @media (min-width: 1200px) { #sourcecode .affix, #sourcecode .affix-bottom { width: 263px !important; position: static !important; } .col-md-9 h1:first-child { margin-top: 40px !important; } } @media (min-width: 992px) { #sourcecode .affix, #sourcecode .affix-bottom { width: 213px; } #sourcecode .affix { position: static !important; } .col-md-9 h1:first-child { margin-top: 40px !important; } } @media (min-width: 768px) { #sourcecode .affix { position: static; } .col-md-9 h1:first-child { margin-top: 0px; } } @media (min-width: 480px) { #sourcecode .affix { position: static; } .col-md-9 h1:first-child { margin-top: 0px; } } @media (max-width: 480px) { #sourcecode .affix { position: static; } .col-md-9 h1:first-child { margin-top: 0px; } } #source-content ul.sections > li > div.content { background: #f5f5f5; } #source-content div.content { padding: 13px; border-radius: 6px; } #source-content div.annotation { padding: 10px 25px 1px 0px; } dd { padding-left: 2em; } /*MARKDOWN*/ #markdownpages h1 { margin-bottom: 20px; border-bottom: 1px solid rgba(0,0,0,0.1); padding-bottom: 17px; } #markdownpages h2 { margin-top: 30px; margin-bottom: 20px; font-size: 36px; line-height: 42px; } #markdownpages h3 { font-size: 24px; margin: 20px 0px; } #markdownpages h4 { font-size: 18px; margin: 20px 0px; } #markdownpages code { white-space: pre-wrap; } #markdownpages pre { margin: 10px 0 20px; } #markdownpages ul li { font-size: 14px; font-family: 'Open Sans',sans-serif; } #markdownpages #sidenav { margin-top: 20px; } #sidenav .well .btn { margin-bottom: 10px; } #sectionmenu { border: 1px solid rgba(0,0,0,0.1); margin-bottom: 20px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } /*this doesnt match up right*/ /*#sectionmenu li.active { text-decoration: underline; }*/ .panel { border:0px; box-shadow: none; background: none; } #sidenav.panel .panel-heading { background: #fff; border-bottom: none; font-weight: 700; } #sidenav .affix { width: 263px; position: fixed; } .affix-bottom { top: -200px !important; } @media (min-width: 1200px) { #markdownpages h1 { font-weight: normal; } #sourcecode h1 { font-weight: normal; } .affix, .affix-bottom { width: 263px !important; position: fixed !important; bottom: 10px !important; } .col-md-9 h1:first-child { margin-top: 40px !important; } } @media (min-width: 992px) { .affix, .affix-bottom { width: 213px; } .affix { position: fixed !important; } .col-md-9 h1:first-child { margin-top: 40px !important; } } @media (min-width: 768px) { #sketchapp a.tjs-button { float: right !important; margin-bottom: 20px; } .affix { position: static; } .col-md-9 h1:first-child { margin-top: 0px; } } @media (min-width: 480px) { #sketchapp a.tjs-button { float: right !important; margin-bottom: 20px; } #sourcecode h1 { font-size: 48px; line-height: 52px; } #markdownpages h1 { font-size: 48px; } .affix { position: static; } .col-md-9 h1:first-child { margin-top: 0px; } } @media (max-width: 480px) { #sketchapp a.tjs-button { float: left !important; margin-bottom: 20px; } #sourcecode h1 { font-size: 48px; line-height: 52px; } #markdownpages h1 { font-size: 48px; line-height: 52px; } .affix { position: static; } .col-md-9 h1:first-child { margin-top: 0px; } } ================================================ FILE: site/docs/contributing.md ================================================ # Contributing to TogetherJS Here are a variety of notes about contributing to the TogetherJS codebase. This isn't stuff that applies if you are simply integrating TogetherJS in your site. ## Code Style [Here is a style document](https://github.com/ianb/javascript). It's a fork of the [Airbnb](https://github.com/airbnb/javascript) style guide, and maybe takes a little from the [Mozilla style guide](https://developer.mozilla.org/en-US/docs/Developer_Guide/Coding_Style), and then a little of our own opinions. ### Code Cleanliness Please figure out how to get your editor to delete trailing whitespace! It's a nuisance and creates useless diffs. Files should also end with a newline. ## TogetherJS Patterns ### Modules There are [some notes in the style guide](https://github.com/ianb/javascript#modules). TogetherJS uses [requirejs](http://requirejs.org/) for module loading, and the AMD pattern generally. Each module should go in `app/http/togetherjs/public/` and look like: ```javascript define(["util", "jquery", "require"], function (util, $, require) { var myModule = util.Module("myModule"); var assert = util.assert; myModule.object = ... // and so on return myModule; }); ``` The first list is the dependencies (modules) you need. If you need to require module modules later (lazily, or later than load time due to circular dependencies) you must include `require` among your dependencies. There *is* a global `require()` object, but you can't use it, because TogetherJS uses a [context](http://requirejs.org/docs/api.html#multiversion). You should define an object with a name matching the name of the module (and filename). This way an object or function will be named the same throughout the project, both when used internally and externally (e.g., `myModule.object`), and when it is being created. If you want to load a module from the console, you do it like this: ```javascript session = require({context: "togetherjs"})("session"); ``` (This form of `require` only works when the module is already loaded, but from the console that's usually the case.) ### Classes There is a class factory in `util.Class`. It supports subclassing, but we haven't used any subclasses yet, and I'm not sure we will. Look at the implementation if you want to know more. To define a class, do: ```javascript var MyClass = util.Class({ constructor: function (...) {...} }); MyClass.classMethod = function (...) {...}; var instance = MyClass(); ``` The `constructor` function is called when the object is instantiated. You don't need to use `new` when creating instances, and really you shouldn't (it'll just create an object that'll be thrown away). MyClass.prototype is what you would expect. ### this You should understand how `this` is bound, and how that binding is lost. Generally we prefer using `.bind(this)` to keep the references to this (as opposed to using `self = this`). For example: ``` var MyClass = Class({ goodExample: function () { doSomething((function () {return this.foo;}).bind(this)); }, badExample: function () { var self = this; doSomething(function () {return self.foo;}); } }); ``` Why? Mostly so that `this` is always called `this`, and you don't have to figure out whether or where there is an alias. ### Templating Right now we aren't using any real templating system. Almost everything is in `app/http/views/interface.html` – and most likely any new markup should also go there. We try to keep most of the code that actually touches the ui in `ui.js` – though it's not done very strictly. Moving stuff back into ui.js is appreciated though. "Templates" are just elements that are cloned out of `interface.html`. They generally have an id like `togetherjs-template-foo`, and you'd clone them by doing `templating.sub("foo", {vars})`. You should avoid having text or markup in JavaScript, and instead clone templates or hide and show different elements to represent different states. Occasionally you do need to put markup-related stuff in code (like pixel sizes or other details). When this is the case leave a comment in both sources (HTML/CSS and JS) pointing to the other location. ### Stateless controls Instead of "toggle" controls we prefer to show and hide controls for the alternate states. For instances, if you have a expand/collapse control: ```html ``` Then use `ui.displayToggle("#togetherjs-expand-foo")` to show the expand button, and the collapse button will be automatically hidden. Note that the selector in `data-toggles` can be inclusive of the element itself (everything matching selector will be hidden, except the element itself). This requires less state in the JavaScript, as the control is always an assertion to do something specific. Also it means that we have to do a minimum of manipulation in JavaScript, and the two controls are not required to be styled identically. ### Async Use [jQuery.Deferred](http://api.jquery.com/category/deferred-object/) when possible. This is exposed as `util.Deferred` ## Hosting the Hub Server This has been [moved to the main docs](./#hosting-the-hub-server). ## The Issue Tracker and Milestones We do most of our planning in the [Github issue tracker](https://github.com/mozilla/togetherjs/issues), and make use of the [Milestones](https://github.com/mozilla/togetherjs/issues/milestones) (more than labels). You are welcome to just submit issues without worrying about this, but if you are looking at a ticket and want how it relates to our plans then you might want to know our system. Generally we have three running milestones: * "Release X" or "Beta 1039", etc: something that represents our planned work for the current iteration. We don't generally complete everything we plan for in an iteration (i.e., we err on the side of including stuff in a milestone), so if something is important to you then you might still want to note this in a ticket. * [Next Tasks](https://github.com/mozilla/togetherjs/issues?milestone=17&state=open): this is a long-standing milestone that represents tasks we want to do soon, but not in the current milestone. Typically when planning the next iteration we'll look through this milestone and pick out issues. If something in this milestone is a priority for you, please note that in a comment on the ticket so we can understand your needs. * [Blue Sky](https://github.com/mozilla/togetherjs/issues?milestone=23&state=open): another long-standing milestone, this represents stuff we'd like to do but don't have any plans to do any time soon. If you are looking to contribute this is an excellent place to look for ideas. And if it's something you think would be helpful to you, a comment would be good -- especially one that outlines a use case. * [Issues with no milestone](https://github.com/mozilla/togetherjs/issues?milestone=none&page=1&state=open): these are issues that haven't been triaged. We try to assign milestones every week to these issues. If we don't then probably we are caught up in something we need to focus on over a short period, or we're just all out on vacation or something. ================================================ FILE: site/docs/faq.md ================================================ # FAQ ## When I add a field dynamically it doesn't appear on the other person's page! ## Will I see what the other person is seeing? ## Why not synchronize everything? ## Should I host my own server? ## How stable is TogetherJS? ## Does it work on an intranet? ================================================ FILE: site/docs/index.md ================================================ # Documentation Would you like to use TogetherJS on your site? Great! If you have feedback on this or any other part of TogetherJS [we'd like to hear it](https://docs.google.com/forms/d/1lVE7JyRo_tjakN0mLG1Cd9X9vseBX9wci153z9JcNEs/viewform)! ## Quick Start Get started quickly by including two things on your page. First the JavaScript: ```html ``` You can put that wherever; e.g., right before ``. The next step is to put a button on your site that lets a user start TogetherJS: ```html ``` Or if you don't like `onclick`: ```html ``` Calling `TogetherJS()` will start the tool, or stop the tool if it is already started. You should put the `togetherjs-min.js` script on every page in your site, even if you don't include the "Start TogetherJS" button. As long as the script is on a page then two people can collaborate on that page. If you forget it on a page, then if someone visits that page while in a TogetherJS session they will essentially go "offline" until they come back to another page that includes `togetherjs-min.js` Note that `togetherjs-min.js` *is not* the entire code for TogetherJS, it's only a fairly small file that loads the rest of TogetherJS on demand. You can place the ` ``` The other way to set a variable *after* TogetherJS is loaded is `TogetherJS.config("variable", value)`. Some variables cannot be updated after TogetherJS has started, but if this is the case then you should get an error. ### The Configuration `TogetherJSConfig_siteName`: This is the name of your site. It defaults to the title of the page, but often a more abbreviated title is appropriate. This is used in some help text. `TogetherJSConfig_toolName`: This is the name that you are giving this tool. If you use this then "TogetherJS" won't be in the UI. So you might want to use `TogetherJSConfig_toolName = "Collaboration". `TogetherJSConfig_hubBase`: This is where the hub lives. The hub is a simple server that echoes messages back and forth between clients. It's the only real server component of TogetherJS (besides the statically hosted scripts). It's also really boring. If you wanted to use a hub besides ours you can override it here. The primary reason would be for privacy; though we do not look at any traffic, by hosting the hub yourself you can be more assured that it is private. You'll find that a hub with a valid https certificate is very useful, as mixed http/https is strictly forbidden with WebSockets. `TogetherJSConfig_dontShowClicks`: This should be set to a jQuery selector or set to true. This will disable the visual click display indicating that a user has clicked on the defined element. For example: "canvas", "h1", "p", etc. Setting `TogetherJSConfig_dontShowClicks = true` will globally disable all clicks. `TogetherJSConfig_cloneClicks`: This should be set to a jQuery selector or set to true. Whenever someone clicks on an element matching this selector, that click will be repeated (as an actual click) on everyone else's browser. This is useful for cases when a click typically doesn't *do* anything, but shows or hides or switches the view of the page. Note that any control that toggles will definitely not work here! If you have tab buttons that show different things you might use `TogetherJSConfig_cloneClicks = ".tab"`. Setting `TogetherJSConfig_cloneClicks = true` will globally clone clicks. `TogetherJSConfig_enableShortcut`: If you want to try TogetherJS out on an application, but don't want to put up a "Start TogetherJS" button, you can use `TogetherJSConfig_enableShortcut = true` and then an event handler will be put into place that will start TogetherJS when you hit **alt-T alt-T** (twice in a row!). TogetherJS will still automatically start when someone opens an invitation link. `TogetherJSConfig_useMinimizedCode`: Typically if you use `togetherjs.js` you'll get the unminimized and uncombined code, with each module loaded lazily. If you use `togetherjs-min.js` you get the combined code. But if you want to override that more dynamically, you can use this setting. `TogetherJSConfig_findRoom`: To see this in action, check out the examples. This setting assigns people to a room. If you use a single string, this will be the name of the room; for instance: `TogetherJSConfig_findRoom = "my_site_com_users"`. You can also assign people to a series of rooms with maximum occupancy (what our examples do): `TogetherJSConfig_findRoom = {prefix: "my_site_com_users", max: 5}` **Note:** if you change this setting, test in a *new tab* (old browser tabs carry session information that will confuse you). `TogetherJSConfig_autoStart`: If true then start TogetherJS automatically. Note TogetherJS already starts automatically when a session is continued, so if you just always call `TogetherJS()` then you might cause TogetherJS to stop. Note you must set this as `TogetherJSConfig_autoStart = true`, not using `TogetherJS.config("autoStart", true)` (it must be set when TogetherJS loads). Anyone who autostarts a session will not be considered the session creator. `TogetherJSConfig_suppressJoinConfirmation`: When a person is invited to a session, they'll be asked if they want to join in browsing with the other person. Set this to `true` and they won't be asked to confirm joining. Useful when combined with `findRoom`. `TogetherJSConfig_suppressInvite`: When a person starts a session, usually a window will pop open with the invite link so they can send it to someone. If this is true then that window doesn't automatically pop open (but it is still available). `TogetherJSConfig_inviteFromRoom`: This adds the ability from the profile menu to invite people who are hanging out in another room (using TogetherJS). This is kind of (but not exactly) how the "Get Help" button works on this site. This is still an experimental feature. `TogetherJSConfig_includeHashInUrl`: When true (default false), TogetherJS treats the entire URL, including the hash, as the identifier of the page; i.e., if you one person is on `http://example.com/#view1` and another person is at `http://example.com/#view2` then these two people are considered to be at completely different URLs. You'd want to use this in single-page apps where being at the same base URL doesn't mean the two people are looking at the same thing. `TogetherJSConfig_disableWebRTC`: Disables/removes the button to do audio chat via WebRTC. `TogetherJSConfig_youtube`: If true, then YouTube videos will be synchronized (i.e., when one person plays or pauses a video, it will play for all people). This will also load up the YouTube iframe API. `TogetherJSConfig_ignoreMessages`: Contains a list of all the messages that will be ignored when console logging. Defaults to the list ["cursor-update", "keydown", "scroll-update"]. Will ignore all messages if set to true. `TogetherJSConfig_ignoreForms`: Contains a list of all the forms that will be ignored. Each item of the list is a jQuery selector matching the form element to be ignored. Defaults to [":password"]. Will ignore all forms if set to true. There are additional hooks you can configure, which are described in [Extending TogetherJS](#extending-togetherjs). ## Start TogetherJS Button The button you add to your site to start TogetherJS will typically look like this: ```html ``` 1. If you give your button the same `id` across your site, TogetherJS will know what the start/end TogetherJS button is. It's not essential, but TogetherJS uses this to zoom the controls into and out of the button. 2. `onclick="TogetherJS(this); return false"` – this starts TogetherJS, and by passing `this` TogetherJS knows what button it started from. This lets it animate out of the button. It'll also work fine with `document.getElementById("start-togetherjs").addEventListener("click", TogetherJS, false)` 3. `data-end-togetherjs-html` is what TogetherJS will insert into the content of the button after it is started. You can use this to switch Start to End, or whatever language you use. As a special case "Start TogetherJS" is changed to "End TogetherJS". 4. The class `togetherjs-started` will be added to the button while TogetherJS is active. You might want to use this to style the background color to red to show that it changes to ending the session. ### Scope of the session TogetherJS sessions are connected to the domain you start them on (specifically the [origin](http://tools.ietf.org/html/rfc6454)). So if part of your site is on another domain, people won't be able to talk across those domains. Even a page that is on https when another is on http will cause the session to be lost. We might make this work sometime, but if it's an issue to you please give us [feedback](https://docs.google.com/forms/d/1lVE7JyRo_tjakN0mLG1Cd9X9vseBX9wci153z9JcNEs/viewform). ## About Audio Chat and WebRTC The live audio chat is based on [WebRTC](http://www.webrtc.org/). This is a very new technology, built into some new browsers. To enable WebRTC both you and your collaborator need a new browser. Right now, [Firefox Nightly](http://nightly.mozilla.org/) is supported, and we believe that the newest release of Chrome should work. Sometime in 2013 support for this should be available in new (non-experimental) versions of Firefox, Chrome, and both Firefox and Chrome for Android. To see a summary of outstanding issues that we know of with audio chat see [this page](https://github.com/mozilla/togetherjs/issues?labels=rtc&milestone=&page=1&state=open). Note that audio chat will not work between some networks. These networks require a [TURN server](http://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) which unfortunately we do not have allocated (and full support for TURN has not landed in some browsers). Unfortunately when the network makes chat impossible, chat will simply not work – we don't receive an error, and can't tell you why chat is not working. See [#327](https://github.com/mozilla/togetherjs/issues/327) for progress. ## Extending TogetherJS While [configuration](#configuring-togetherjs) covers some of what you can do to customize TogetherJS, you may also need to integrate TogetherJS with your application, or sync your application data between clients. ### Configuring events Like other configuration, you can configure the event handlers and hooks we describe before `togetherjs(-min).js` is loaded. Event handlers are just a smidge different. You'd normally add event handler like `TogetherJS.on("ready", function () {...})`. To do it as configuration: ```js TogetherJSConfig_on = { ready: function () {} }; ``` Or if you want to set things up one-by-one you can do: ```js TogetherJSConfig_on_ready = function () {}; ``` Additionally you may want to add event listeners to `TogetherJS.hub`; these are done like: ```js TogetherJS_hub_on = { "my-event": function (msg) { } }; ``` ### Communication Channel If you have a component you want to synchronize between two clients, you'll want to use the TogetherJS communication channel. This is a broadcast channel – any message you send is sent to everyone else in the session (which can also be no one), and includes people who are on different pages. All messages are JSON objects with a `type` property. Custom application messages are put into their own namespace. So imagine you want to keep an element hidden or visible on all clients, in a synchronized way, and when the element visibility changes an event is fired inside your app, `MyApp.emit("visibilityChange", element, isVisible)`: ```js TogetherJSConfig_on_ready = function () { MyApp.on("visibilityChange", fireTogetherJSVisibility); }; TogetherJSConfig_on_close = function () { MyApp.off("visibilityChange", fireTogetherJSVisibility); }; ``` Now when TogetherJS is activated we'll call `fireTogetherJSVisibility(el, isVisible)`. Now we have to write that function: ```js function fireTogetherJSVisibility(element, isVisible) { TogetherJS.send({type: "visibilityChange", isVisible: isVisible, element: element}); } ``` Well, that's not quite right, we have to send a JSON object, and we can't send `element`. Instead we need to give an identifier for the element. TogetherJS has a helpful function for that, which will require us to import the `elementFinder` module: ```js function fireTogetherJSVisibility(element, isVisible) { var elementFinder = TogetherJS.require("elementFinder"); var location = elementFinder.elementLocation(element); TogetherJS.send({type: "visibilityChange", isVisible: isVisible, element: location}); } ``` Then we also have to listen for the message. We can setup this listener right away (without using the ready/close TogetherJS events) because when TogetherJS isn't on then the event will just not fire: ```js TogetherJS.hub.on("visibilityChange", function (msg) { var elementFinder = TogetherJS.require("elementFinder"); // If the element can't be found this will throw an exception: var element = elementFinder.findElement(msg.element); MyApp.changeVisibility(element, msg.isVisible); }); ``` This has two major problems though: when you call `MyApp.changeVisibility` it will probably fire a `visibilityChange` event, which will cause another `fireTogetherJSVisibility` call. The result may or may not be circular, but it's definitely not efficient. Another problem is that you can get messages from peers who are at a different URL. We'll use a simple global variable to handle the first case, and `msg.sameUrl` to fix the second: ```js var visibilityChangeFromRemote = false; function fireTogetherJSVisibility(element, isVisible) { if (visibilityChangeFromRemote) { return; } var elementFinder = TogetherJS.require("elementFinder"); var location = elementFinder.elementLocation(element); TogetherJS.send({type: "visibilityChange", isVisible: isVisible, element: location}); } TogetherJS.hub.on("visibilityChange", function (msg) { if (! msg.sameUrl) { return; } var elementFinder = TogetherJS.require("elementFinder"); // If the element can't be found this will throw an exception: var element = elementFinder.findElement(msg.element); visibilityChangeFromRemote = true; try { MyApp.changeVisibility(element, msg.isVisible); } finally { visibilityChangeFromRemote = false; } }); ``` Now we're getting close, except for one last problem: these events sync everything when the users are on the same page, but there may be a late comer whose page won't be in sync with everything else. An event `togetherjs.hello` will fire when a person appears on a new page, and we can use to that send all our state. To do this we'll imagine the `MyApp` object has a function like `MyApp.allToggleElements()` that returns a list of elements that we'd be expected to sync. ```js TogetherJS.hub.on("togetherjs.hello", function (msg) { if (! msg.sameUrl) { return; } MyApp.allToggleElements.forEach(function (el) { var isVisible = $(el).is(":visible"); fireTogetherJSVisibility(el, isVisible); }); }); ``` You'll notice that multiple clients might do this reset. This is an open question for us, and in the future we'll provide a higher-level API for this kind of initialization. #### Implementing those visibility function from jQuery Let's say your app doesn't have all these methods, and you are just using plain ol' jQuery. Here's how you might implement them each; you'll just have to start using `$(el).syncShow()` and `$(el).syncHide()` to do your showing and hiding: ```js $.fn.syncShow = function () { this.show(); this.trigger("visibilityChange"); }; $.fn.syncHide = function () { this.hide(); this.trigger("visibilityChange"); }; $(document).on("visibilityChange", function () { MyApp.emit("visibilityChange", this, $(this).is(":visible")); }); MyApp.changeVisibility = function (el, isVisible) { if (isVisible && ! el.is(":visible")) { el.syncShow(); } else if ((! isVisible) && el.is(":visible")) { el.syncHide(); } }; ``` ### Setting identity information There's a good chance your application has its own identity, and you know the name of the user, and perhaps have an avatar. (If you don't have an avatar but do have an email, you might want to use that to make a Gravatar.) To do this you configure TogetherJS with some functions: `TogetherJSConfig_getUserName = function () {return 'User Name';};` This returns the user's name (or nick). Return null if you can't determine the name. `TogetherJSConfig_getUserAvatar = function () {return avatarUrl;};` This returns a URL to the user's avatar. It should be 40px square. Again return null if you aren't sure. `TogetherJSConfig_getUserColor = function () {return '#ff00ff';};` This returns the user's preferred color that represents them. This should be a CSS color. The names might confuse you: you are providing functions that TogetherJS will call to get the user's name, avatar, and color. It doesn't return the name the user has set through TogetherJS (that would be `TogetherJS.require("peers").Self.name`). If any of these values are updated while in the page (like if you have a login process that doesn't cause a page reload) then call `TogetherJS.refreshUserData()` and the respective `getUser*` callbacks will all be called again. See [#504](https://github.com/mozilla/togetherjs/issues/504) for a bug related to improving this support. ### TogetherJS.reinitialize() You can run this to try to reinitialize anything TogetherJS initializes on page load. In particular you can use it if there are new code editors or video elements that should be sync'd, but were added dynamically to the page. E.g.: ```javascript $("#content").append('