Repository: sourcejs/Source Branch: master Commit: 6b3f8969b800 Files: 219 Total size: 959.3 KB Directory structure: gitextract_hig6wl4x/ ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CONTRIBUTING.md ├── Gruntfile.js ├── License.md ├── MAINTAINING.md ├── README.md ├── app.js ├── appveyor.yml ├── assets/ │ ├── css/ │ │ ├── base/ │ │ │ ├── buttons.less │ │ │ ├── grids.less │ │ │ ├── navigation.less │ │ │ ├── ntf.less │ │ │ └── togglers.less │ │ ├── clarify.bundle.less │ │ ├── cosmetic/ │ │ │ ├── helpers.less │ │ │ ├── highlights.less │ │ │ └── links.less │ │ ├── defaults.less │ │ ├── mixins.less │ │ ├── project/ │ │ │ ├── auth.less │ │ │ ├── autocomplete.less │ │ │ ├── catalog.less │ │ │ ├── clarify-in-spec.less │ │ │ ├── clarify.less │ │ │ ├── examples.less │ │ │ ├── footer.less │ │ │ ├── header.less │ │ │ ├── info.less │ │ │ ├── layout.less │ │ │ ├── modal.less │ │ │ ├── navigation.less │ │ │ ├── search.less │ │ │ ├── section.less │ │ │ └── source-code.less │ │ ├── reset.less │ │ ├── source.css │ │ └── variables.less │ ├── fonts/ │ │ └── Webfont EULA 1.6.txt │ ├── js/ │ │ ├── _require.bundle.js │ │ ├── clarify.js │ │ ├── enter-the-source.js │ │ ├── lib/ │ │ │ ├── autocomplete.js │ │ │ ├── codeFormat.js │ │ │ ├── html5shiv.js │ │ │ ├── jquery.autocomplete.js │ │ │ ├── jquery.couch.js │ │ │ ├── jquery.mb.browser.js │ │ │ ├── lodash.js │ │ │ ├── modalbox.js │ │ │ ├── prism/ │ │ │ │ ├── prism.css │ │ │ │ └── prism.js │ │ │ ├── require.js │ │ │ └── text.js │ │ ├── load-options.js │ │ ├── modules/ │ │ │ ├── auth.js │ │ │ ├── browser.js │ │ │ ├── clarifyInSpec.js │ │ │ ├── codeSource.js │ │ │ ├── couch.js │ │ │ ├── css.js │ │ │ ├── globalNav.js │ │ │ ├── headerFooter.js │ │ │ ├── htmlAPISync.js │ │ │ ├── inlineOptions.js │ │ │ ├── innerNavigation.js │ │ │ ├── loadEvents.js │ │ │ ├── module.js │ │ │ ├── moduleLoader.js │ │ │ ├── navHighlight.js │ │ │ ├── ntf.js │ │ │ ├── parseFileTree.js │ │ │ ├── scrollToHash.js │ │ │ ├── search.js │ │ │ ├── sectionFolding.js │ │ │ ├── sections.js │ │ │ ├── sectionsParser.js │ │ │ ├── specDecorations.js │ │ │ ├── trimSpaces.js │ │ │ └── utils.js │ │ └── require-config.js │ ├── templates/ │ │ ├── clarifyPanel.inc.html │ │ ├── footer.inc.html │ │ ├── header.inc.html │ │ ├── nav.inc.html │ │ ├── navActionItem.inc.html │ │ └── navActionTumbler.inc.html │ └── test/ │ ├── index.html │ ├── jasmine/ │ │ ├── MIT.LICENSE │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ └── jasmine.js │ ├── js/ │ │ └── tests.js │ └── spec/ │ ├── innerNavigationSpec.js │ ├── moduleSpec.js │ └── sectionsSpec.js ├── core/ │ ├── api/ │ │ ├── index.js │ │ └── optionsApi.js │ ├── auth.js │ ├── ejsWithHelpers.js │ ├── file-tree/ │ │ └── index.js │ ├── headerFooter.js │ ├── html-tree/ │ │ ├── html-parser/ │ │ │ ├── index.js │ │ │ └── phantomRunner.js │ │ └── index.js │ ├── lib/ │ │ ├── configUtils.js │ │ ├── createLink.js │ │ ├── extendTillSpec.js │ │ ├── flattenTillSpec.js │ │ ├── parseData.js │ │ ├── pathResolver.js │ │ ├── processMd.js │ │ ├── specUtils.js │ │ ├── translit.js │ │ ├── utils.js │ │ └── viewResolver.js │ ├── loadOptions.js │ ├── loadPlugins.js │ ├── logger.js │ ├── middlewares/ │ │ ├── clarify.js │ │ ├── loader.js │ │ ├── md.js │ │ ├── mdTag.js │ │ ├── read.js │ │ ├── send.js │ │ └── wrap.js │ ├── postInstall.js │ ├── routes/ │ │ ├── index.js │ │ └── redirects.js │ ├── trackInstall.js │ ├── trackStats.js │ ├── unflat.js │ ├── views/ │ │ ├── 404.ejs │ │ ├── auth-done.ejs │ │ ├── clarify/ │ │ │ ├── clear.ejs │ │ │ └── default.ejs │ │ ├── clean-spec.ejs │ │ ├── doc.ejs │ │ ├── navigation.ejs │ │ └── spec.ejs │ └── watch/ │ ├── childWatch.js │ └── index.js ├── docs/ │ ├── README.md │ ├── api/ │ │ ├── index.src.html │ │ ├── info.json │ │ ├── load-events/ │ │ │ ├── info.json │ │ │ └── readme.md │ │ ├── plugins/ │ │ │ ├── info.json │ │ │ └── readme.md │ │ ├── readme.md │ │ └── rest-api/ │ │ ├── info.json │ │ └── readme.md │ ├── auth/ │ │ ├── info.json │ │ └── readme.md │ ├── base/ │ │ ├── info.json │ │ └── readme.md │ ├── build-tasks/ │ │ ├── info.json │ │ └── readme.md │ ├── clarify/ │ │ ├── info.json │ │ └── readme.md │ ├── configuration/ │ │ ├── info.json │ │ └── readme.md │ ├── data/ │ │ └── bootstrap.css │ ├── data-nav/ │ │ ├── example/ │ │ │ ├── info.json │ │ │ └── readme.md │ │ ├── info.json │ │ └── readme.md │ ├── index.src.html │ ├── info-json/ │ │ ├── info.json │ │ └── readme.md │ ├── info.json │ ├── markdown/ │ │ ├── info.json │ │ └── readme.md │ ├── migration/ │ │ ├── info.json │ │ └── readme.md │ ├── spec/ │ │ ├── css/ │ │ │ └── spec.css │ │ ├── index.src.html │ │ └── info.json │ ├── spec-helpers/ │ │ ├── examples/ │ │ │ ├── buttons.html │ │ │ ├── include.html │ │ │ ├── markdown-file.md │ │ │ ├── mask-one.html │ │ │ ├── mask-two.html │ │ │ └── three.html │ │ ├── info.json │ │ └── readme.md │ ├── starting/ │ │ ├── css/ │ │ │ └── starting.css │ │ ├── index.src.html │ │ └── info.json │ ├── starting-md/ │ │ ├── css/ │ │ │ └── starting.css │ │ ├── info.json │ │ └── readme.md │ └── test-specs/ │ └── styles/ │ ├── includes/ │ │ └── all-tags.html │ ├── index.src.html │ └── info.json ├── options.js ├── package.json ├── source.sh └── test/ ├── data/ │ ├── api-test-html.json │ └── api-test-specs.json ├── functional/ │ ├── common.js │ ├── globalNav.js │ ├── search.js │ ├── snippets.md │ └── specpage.js └── unit/ ├── api/ │ ├── html.js │ └── specs.js ├── ejsHelpersSpec.js ├── lib/ │ ├── extendTillSpec.js │ └── utils.js ├── middleware/ │ ├── clarify.js │ └── md.js └── partials/ ├── markdown-ejs-nested.md ├── markdown-ejs.md ├── markdown.md ├── mask-one.html ├── mask-two.html └── three.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.idea *.iml *.DS_Store node_modules npm-debug.log # Generated data for API /core/api/data /log # Generated JSdoc /jsdoc # Build folder for assets /build # Web server root, and your specs home # You can store other repository in this path /user ================================================ FILE: .jshintrc ================================================ { "eqeqeq": true, "expr": true, "forin": true, "immed": true, "latedef": true, "smarttabs": true, "strict": true, "sub": true, "newcap": true, "noarg": true, "nonew": false, "trailing": true, "undef": true, "unused": "vars", "devel": true, "laxbreak": true, "node": true, "browser": true, "scripturl": true, "jquery": true, "predef": [ "define", "Prism", "SourceGetSections"] } ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "0.10" - "0.11" - "0.12" sudo: false notifications: email: on_success: never on_failure: always before_script: "git clone https://github.com/sourcejs/init.git user" script: npm run ci-test ================================================ FILE: CONTRIBUTING.md ================================================ # How to become a contributor and submit your own code ## Contributing A Patch 1. Submit an issue describing your proposed change to the repo in question. 1. The repo owner will respond to your issue promptly. 1. Fork the desired repo, develop and test your code changes. 1. Submit a pull request to next dev branch (for example 0.5.1-dev). ## Protocols for Collaborative Development Please read [this doc](MAINTAINING.md) for information on how we're running development for the project. # Additional Resources * [@SourceJS](https://twitter.com/SourceJS) * [Web site](http://sourcejs.com) * [General documentation](http://sourcejs.com/docs/) * [Quick Start guide](http://sourcejs.com/docs/base/) * [More information on contributing](MAINTAINING.md) ## Team members All inquiries should be forwarded to [Robert Haritonov](mailto:r@rhr.me). Core contributors and maintainers: * [Robert Haritonov](https://github.com/operatino) * [Ilya Mikhailov](https://github.com/cheshirsky) ================================================ FILE: Gruntfile.js ================================================ 'use strict'; var path = require('path'); var fs = require('fs'); var pathToApp = path.resolve('./'); global.pathToApp = pathToApp; var loadOptions = require('./core/loadOptions'); // NPM 3 compatibility fix var getLoaderPackageName = function() { var packageName; var parentFolderName = path.basename(path.resolve('..')); var isSubPackage = parentFolderName === 'node_modules'; var isLocalDepsAvailable = fs.existsSync('node_modules/grunt-autoprefixer') && fs.existsSync('node_modules/grunt-contrib-cssmin'); if (isSubPackage && !isLocalDepsAvailable) { packageName = 'load-grunt-parent-tasks'; } else { packageName = 'load-grunt-tasks'; } return packageName; }; module.exports = function(grunt) { var appPort = grunt.option('app-port') || 8080; // load all grunt tasks matching the `grunt-*` pattern require(getLoaderPackageName())(grunt); // measuring processing time require('time-grunt')(grunt); grunt.initConfig({ options: loadOptions(pathToApp), pathToUser: '<%= options.core.common.pathToUser %>', banner:'/*!\n' + '* SourceJS - Living Style Guides Engine and Integrated Maintenance Environment for Front-end Components.\n' + '* @copyright 2013-2015 Sourcejs.com\n' + '* @license MIT license: http://github.com/sourcejs/source/wiki/MIT-License\n' + '* */\n', // clean files after build clean: { build: [ 'build' ] }, jshint: { options: { jshintrc: ".jshintrc" }, gruntfile: ["Gruntfile.js"], modules: ["assets/js/modules/**/*.js"], core: [ "app.js", "core/**/*.js" ] }, copy: { js: { expand: true, dest: 'build', src: [ 'assets/js/**/*.js', '<%= pathToUser %>/assets/js/**/*.js', '!assets/js/**/_*.js', '!<%= pathToUser %>/assets/js/**/_*.js' ] } }, uglify: { main: { options: { preserveComments: 'some' }, expand: true, dest: './', src: [ 'build/**/*.js' ] } }, cssmin: { user: { options: { noAdvanced: true }, expand: true, dest: 'build', src: [ '<%= pathToUser %>/assets/**/*.css' ] }, // For processing files after preprocessors build: { options: { noAdvanced: true }, expand: true, dest: 'build', cwd: 'build', src: ['**/*.css'] } }, htmlmin: { main: { options: { removeComments: true, collapseWhitespace: true }, expand: true, dest: 'build', src: [ 'assets/templates/**/*.html', '<%= pathToUser %>/assets/templates/**/*.html' ] } }, less: { main: { files: { "build/assets/css/defaults.css": "assets/css/defaults.less" } }, bundles: { expand: true, dest: 'build/assets/css', cwd: 'assets/css', src: ['**/*.bundle.less'], ext: '.bundle.css' } }, jsdoc: { core: { src: ['core/**/*.js'], options: { destination: 'jsdoc/core' } }, assets: { src: [ 'assets/js/**/*.js', '!assets/js/lib/**/*.js' ], options: { destination: 'jsdoc/assets' } } }, watch: { css: { files: [ 'assets/css/**/*.less' ], tasks: ['less', 'autoprefixer'], options: { nospawn: true } }, js: { files: [ 'assets/js/**/*.js' ], tasks: ['jshint:modules', 'resolve-js-bundles'], options: { nospawn: true } } }, autoprefixer: { options: { cascade: false, browsers: ['last 2 version'] }, main: { expand: true, dest: 'build', cwd: 'build', src: ['**/*.css'] } }, mochaTest: { test: { src: ['test/unit/**/*.js'] }, noApp: { src: ['test/unit/lib/**/*.js'] } }, casperjs: { options: { casperjsOptions: ['--app-port='+appPort] }, files: ['test/functional/**/*.js'] } }); /* * * Custom tasks * * */ grunt.registerTask('clean-build', 'Cleaning build dir if running new type of task', function(){ if ( grunt.file.exists('build/last-run') && (grunt.task.current.args[0] === 'prod' && grunt.file.read('build/last-run') === 'dev' || grunt.task.current.args[0] === 'dev' && grunt.file.read('build/last-run') === 'prod') ) { grunt.task.run('clean:build'); } else { console.log('Skipping clean build dir'); } }); grunt.registerTask('resolve-js-bundles', 'Resolving JS imports in _**.bundle.js', function(){ var gruntOpts = grunt.config.get('options'); // Setting custom delimiters for grunt.template grunt.template.addDelimiters('customBundleDelimiter', '"{%', '%}"'); var files = grunt.file.expand([ 'assets/js/**/_*.bundle.js', '<%= pathToUser %>/assets/js/**/_*.bundle.js' ]); files.map(function(pathToFile){ // **/_*.bundle.js -> build/**/*bundle.js var outputName = path.basename(pathToFile).replace(/^\_/, ""); var outputDir = path.dirname(pathToFile); var outputFullPath = path.join('build', outputDir, outputName); grunt.file.write( outputFullPath, grunt.template.process(grunt.file.read(pathToFile), { delimiters: 'customBundleDelimiter', data: { // npmPluginsEnabled object is filled from loadOptions.js npmPluginsEnabled: JSON.stringify(gruntOpts.assets.npmPluginsEnabled, null, 4) } }) ); grunt.log.ok('Writing to '+outputFullPath); }); }); grunt.registerTask('last-prod', 'Tag build: prod', function(){ grunt.file.write('build/last-run', 'prod'); }); grunt.registerTask('last-dev', 'Tag build: dev', function(){ grunt.file.write('build/last-run', 'dev'); }); /* * * Build tasks * * */ // Regular development update task grunt.registerTask('update', [ 'clean-build:dev', 'resolve-js-bundles', 'less', 'autoprefixer', 'last-dev' ]); grunt.registerTask('default', ['jshint', 'update']); // Prod build, path to minified resources is routed by nodejs server grunt.registerTask('build', [ 'clean-build:prod', 'less', 'autoprefixer', 'newer:cssmin:build', 'newer:cssmin:user', 'resolve-js-bundles', 'newer:copy:js', 'newer:uglify:main', 'newer:htmlmin:main', 'last-prod' ]); grunt.registerTask('watch-css', ['update','watch:css']); grunt.registerTask('watch-all', ['update','watch']); grunt.registerTask('ci-pre-run', [ 'jshint', 'build', 'update' ]); grunt.registerTask('ci-post-run', [ 'test', 'test-func' ]); grunt.registerTask('ci-post-run-win', [ 'test' ]); /* * * Utils * * */ // Test task. Execute with running app grunt.registerTask('test', 'Run ALL tests or specified by second param', function () { // if custom mask set - `grunt test --spec=test/unit/middleware/**/*.js` var spec = grunt.option('spec'); if (spec) { grunt.config.set('mochaTest.test.src', [spec]); grunt.task.run('mochaTest:test'); } else { grunt.task.run('mochaTest'); } }); // Test task. Execute with running app grunt.registerTask('test-func', 'Run ALL functional tests or specified by second param', function () { // if custom mask set - `grunt test --spec=test/unit/middleware/**/*.js` var spec = grunt.option('spec'); if (spec) { grunt.config.set('casperjs.files', [spec]); } grunt.task.run('casperjs'); }); }; ================================================ FILE: License.md ================================================ # The MIT License Copyright © 2013-2016 Sourcejs.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MAINTAINING.md ================================================ # On Collaborative Development. This document represents SourceJS team development agreements. ## Patches welcome First of all: as a potential contributor, your changes and ideas are welcome. Please do not ever hesitate to ask a question or send a PR. ## Code reviews All changes must be code reviewed. For non-maintainers this is obvious, since you can't commit anyway. But even for maintainers, we want all changes to get at least one review, preferably from someone who knows the areas the change touches. For non-trivial changes we may want two or even more reviewers. Most PRs will find reviewers organically. Except for rare cases, such as trivial changes (e.g. typos, comments) or emergencies (e.g. broken builds), maintainers should not merge their own changes. Any maintainer or core contributor who wants to review a PR but does not have time immediately may put a kind of hold on a PR simply by saying so on the PR discussion and offering an ETA. ## Branches and versions Stable project version is on master branch. Upcoming version is on release candidate (RC) branch, which is forked from master and is used to be a base for development. Each release bumps version according to [Semantic Versions specification](http://semver.org/). ## Single feature (issue, etc.) contribution guide for maintainers. * Create new branch which is forked from current Release Candidate branch. * Name it according to the next template: `[developer second name | nickname]/[issue number | task | feature | changeslist name]`. E.g. `smith/search-redesign`. * Develop and test your code changes. Atomic commits and informative commit messages are required. E.g. If you decide to implement code linting task, the list of commit messages can looks like that: - Code linting task configuration is added. Nested node modules are added (see package.json). - Some fixes are implemented due to code linting result. - A couple of additional options are added into linting config. - * merge branch master into smith/code-linting. - Missing parameter is added, some trivial fixes are implemented due to CR feedback. * Merge RC branch into yours, if your changes are implemented. * Create Pull Request from your branch into Release Candidate branch. Please don't forget that PR description should be useful and informative. It is very important for release notes. * Approved PR should be merged into current RC branch. Squashed commits are possible but they aren't preferable. * Merged feature branch should be removed from remote repo. ## Basic agreement points. * Branch naming template: `[developers second name | nickname]/[issue number | task | feature | changes list name]`. * Atomic commits and informative commit messages. * Version bumps according to [specification](http://semver.org/). * Using Pull Requests to apply changes. * PR description should be useful and informative. It is very important for release notes. * Release candidate branches usage for changes and tests. * Github releases and tags usage for each changes list (for RC branches). ### Example: * Current version is `0.4.0` (branch: `master`) * Upcoming version is `0.4.1` (branch: `rc0.4.1`, initially forked from master). * Several developers create their own feature-branches (`rc0.4.1` forks) to implement some features (resolve a couple of issues, etc.).E.g. `smith/code-linting`, `somePerson/middleware-polishing`. * Then changes are implemented they create PR from `nickname/feature` branch to `rc0.4.1`. Last commit in each of brunches is the merge of the RC branch into current one. * Accepted PRs are merged into rc0.4.1. After that merged features are removed from remote repo. * Then the RC branch is ready it becomes a kind of beta, which can be tested or used to create some demos, etc. Some fixes are possible if needed. * New release should be marked by tag with release notes. Release notes text can be formed from PR descriptions. If you have any related questions, contact Robert Haritonov [@operatino](https://github.com/operatino) please. ================================================ FILE: README.md ================================================ # [ON HOLD] SourceJS - Living Style Guide Platform [![npm version](https://badge.fury.io/js/sourcejs.svg)](https://www.npmjs.com/package/sourcejs) [![Build Status](https://travis-ci.org/sourcejs/Source.svg?branch=master)](https://travis-ci.org/sourcejs/Source) [![Windows Build status](https://ci.appveyor.com/api/projects/status/ctvr9lsiaxpbhecm/branch/master?svg=true)](https://ci.appveyor.com/project/operatino/source/branch/master) **The project been stale for a while and currently is in the [ON HOLD] state until we find new maintainer or original author get's back to the design systems field. SourceJS been brought to the public ahead of it's time, and turned out to be too ambitious to handle by a small team of people.** **Today, the ideas SourceJS surfaced are evolving in other open source projects, like [styleguidist](https://react-styleguidist.js.org) and [storybook](https://storybook.js.org).** --- The most advanced tool for documenting, testing and managing Front-end Components achieving productive team work. 🚀 [**Quick Start**](http://sourcejs.com/docs/base) SourceJS powered workflow allows developers to **code new components directly in the documentation.** Combining web components development with documentation and team communication processes, makes SourceJS a powerful tool for Front-end developers and designers. 🎥 Check out short video overview: [![image](http://d.pr/i/7Ax4+)](http://youtu.be/y4KHmX8vCc0) Our main goal is to provide flexible, modular environment for managing reusable Front-end components library. We don't focus on specific technologies, allowing to seamlessly integrate SourceJS workflow with your existing codebase. ___ [**Source engine project page**](http://sourcejs.com)     [**Documentation**](http://sourcejs.com/docs)     [**Examples**](http://sourcejs.com/docs/base/#examples)    [**How-to's**](https://github.com/sourcejs/blog-howto) ___ SourceJS component management engine was originally developed in [OK.ru](https://corp.mail.ru/en/company/social) front-end development team and is recommended for big and middle sized projects. Especially for fast growing web portals, outsource teams with similar project and companies with multiple services. Have questions? Just reach our community through Gitter Chat: [![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-1dce73.svg)](https://gitter.im/sourcejs/Source) ## SourceJS is NOT To clear some confusion around Living Style Guide Platforms comparison, let us define few main differences. ### Static site builder **SourceJS is a dynamic Node.js application**, and does not build static website as Pattern Lab, KSS, StyleDocco are doing. Dynamic environment allows to connect unlimited number of plugins and middlewares for compiling docs, styles, text right on the flight. ### CSS Documentation parser Engine is based on gathering special documentation templates (`index.src.html`, `readme.md` and others), where you leave your HTML examples, template includes and description. All Specs are located in `sourcejs/user/specs` folder, and could contain any catalogue structure, with focus on component folders. **But you can use official SourceJS plugin based on DSS, CSS Documentation parser - [github.com/sourcejs/sourcejs-contrib-dss](https://github.com/sourcejs/sourcejs-contrib-dss).** ## Join the community Many teams are already using SourceJS for building and managing Front-end components libraries for themselves and their clients. To join the community, you just need to follow few simple rules - check our docs about [Maintaining](MAINTAINING.md) and [Contribution](CONTRIBUTING.md). If you notice some bugs, or need to help finding a better solution in your process, feel free to create an issue, and we will solve your problem together. [Materials for presentations](https://github.com/sourcejs/pres). ## Updates * 20.09.15 [0.5.6](https://github.com/sourcejs/Source/releases/tag/0.5.6) and [0.5.6-no-jsdom](https://github.com/sourcejs/Source/releases/tag/0.5.6-no-jsdom) with EJS helpers, NPM 3 support and navigation improvements * 16.08.15 [0.5.5](https://github.com/sourcejs/Source/releases/tag/0.5.5) and [0.5.5-no-jsdom](https://github.com/sourcejs/Source/releases/tag/0.5.5-no-jsdom) patch release with `` tag fix and set of functional tests * 15.08.15 [0.5.4](https://github.com/sourcejs/Source/releases/tag/0.5.4) and [0.5.4-no-jsdom](https://github.com/sourcejs/Source/releases/tag/0.5.4-no-jsdom) with middleware loader, relative paths in navigation support and other improvements * 28.05.15 [0.5.3](https://github.com/sourcejs/Source/releases/tag/0.5.3) context options support, source-as-npm package, CI integration, watcher stability improvements and other great features * 28.05.15 [0.5.3-no-jsdom](https://github.com/sourcejs/Source/releases/tag/0.5.3-no-jsdom) special release without JSDom for Windows users * 15.04.15 [0.5.2](https://github.com/sourcejs/Source/releases/tag/0.5.2) patch release with improved markdown support and `index.src.html` * 28.03.15 [0.5.1](https://github.com/sourcejs/Source/releases/tag/0.5.1) patch release with EJS pre-rendering and various bugfixes * 28.03.15. SourceJS [Bootstrap example bundle](https://github.com/sourcejs/example-bootstrap-bundle) and [How-to articles blog](https://github.com/sourcejs/blog-howto) * 15.03.15. New example [Specs showcase](http://sourcejs.com/specs/) ([source code](https://github.com/sourcejs/example-specs-showcase)) * 15.03.15. CSS Documentation support with [SourceJS DSS plugin](https://github.com/sourcejs/sourcejs-contrib-dss) * 12.03.15. **[0.5.0](https://github.com/sourcejs/Source/releases/tag/0.5.0) release** with full Markdown support, GitHub auth, `info.json` watchers and other improvements * 24.02.15. [0.4.1](https://github.com/sourcejs/Source/releases/tag/0.4.1) patch release * 05.02.15. Mentioned at in-depth [Style Guides Tools overview talk](http://youtu.be/Fr23VpM6wl4ds) * 18.01.15. Published an [intro video about SourceJS](http://youtu.be/y4KHmX8vCc0) * 07.01.15. **[0.4.0](https://github.com/sourcejs/Source/releases/tag/0.4.0) stable release.** From now, we move to fast, semantic release cycle. No globally breaking changes till 1.0.0 * 08.10.14. 0.4.0-rc release, migration [instructions](https://github.com/sourcejs/Source/tree/master/docs/migration) * 01.08.14. [Video review](http://youtu.be/ukFeZnJjrLs?list=PL20zJcC2wnX7RY1CDrKLhSvYxEe6jtMbB) of SourceJS engine and workflow example (RU with EN subtitles) * 31.07.14. 0.4.0-beta release * 01.05.14. Engine presentation from [Front-end Ops Conf](http://www.feopsconf.com/), San Francisco - [Taking Development Tools To The Next Level](http://rhr.me/pres/ime/) with [video](https://www.youtube.com/watch?v=cMIad0zl00I) * 31.01.14. [Preview](http://youtu.be/cefy_U5NU4o) of Source companion tool for prototyping interfaces using existing components * 31.12.13. **0.3.2 release** * 09.10.13. Engine [presentation](http://rhr.me/pres/source-min/) on [Fronteers Jam](http://fronteers.nl/congres/2013/jam-session) ([video](https://vimeo.com/77989211)) * 23.09.13. Published [video recording](http://www.youtube.com/watch?v=3HNW5Bru0Ws) of Source engine presentation from [RIT++](http://ritconf.ru/) 2013 (RU) ## Upcoming updates Respecting open source community, we track all our tasks publicly on GitHub. Follow our [milestones](https://github.com/sourcejs/example-bootstrap-bundle) and twitter announcements [@SourceJS](http://sourcejs.com) to keep in sync with latest plans. List of few global upcoming features * Remove JSDom dependency, for making engine faster and easier to install * SourceJS as npm module official support * Refactored code snippets API and tab view * Integrations with JSDoc and drop-in replacement setup for other Style Guide tools like KSS/Pattern Lab * More screencasts and engine usage demos SourceJS follows semantic versioning and we do our best to keep as less breaking changes as possible. Preparing to 1.0 release, we plan to keep migration path very smooth and painless. So if you will keep in sync with minor releases and API deprecation announcements, you won't face any problems setting up major release updates. ## Useful information ### Browser support SourceJS client-side part is supported in all latest major browsers and IE8+ in [Clarify](http://sourcejs.com/docs/clarify) for testing components. ___ Copyright © 2013-2016 [SourceJS](http://sourcejs.com) Licensed under [MIT License](http://en.wikipedia.org/wiki/MIT_License), read more at [license page](http://github.com/sourcejs/source/wiki/MIT-License). ================================================ FILE: app.js ================================================ /*! * SourceJS - Living Style Guides Engine and Integrated Maintenance Environment for Front-end Components * @copyright 2013-2015 Sourcejs.com * @license MIT license: http://github.com/sourcejs/source/wiki/MIT-License * */ 'use strict'; var express = require('express'); var colors = require('colors'); // jshint ignore:line var fs = require('fs-extra'); var path = require('path'); var commander = require('commander'); var bodyParser = require('body-parser'); var favicon = require('serve-favicon'); /* Globals */ // Define absolute path to app, normalizing windows disk name global.pathToApp = __dirname.replace(/^\w:\\/, function (match) { return match.toLowerCase(); }); var app = global.app = express(); var loadOptions = require('./core/loadOptions'); global.opts = loadOptions(); // Arguments parse */ commander .option('-l, --log [string]', 'Log level (default: ' + global.opts.core.common.defaultLogLevel + ').', global.opts.core.common.defaultLogLevel) .option('-p, --port [number]', 'Server port (default: ' + global.opts.core.server.port + ').') .option('--hostname [string]', 'Server hostname (default: ' + global.opts.core.server.hostname + ').') .option('--html', 'Turn on HTML parser on app start (requires installed and enabled parser).') .option('--test', 'Run app with tests.') .option('--no-watch', 'Run with disabled watcher.') .option('--post-grunt [string]', 'Define Grunt command to run after app start', 'ci-post-run') .parse(process.argv); global.commander = commander; var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); app.set('views', path.join(__dirname, 'core/views')); app.set('user', path.join(__dirname, global.opts.core.common.pathToUser)); // We support `development` (default), `production` and `presentation` (for demos) var MODE = global.MODE = process.env.NODE_ENV || 'development'; global.engineVersion = fs.readJsonSync(path.join(global.pathToApp, '/package.json'), {throws: false}).version; // Default logger var logger = require('./core/logger'); var log = logger.log; global.log = log; if (commander.html) { trackStats.event({ group: 'features', event: 'enabled html parser' }); global.opts.plugins.htmlParser.enabled = true; global.opts.plugins.htmlParser.onStart = true; } if (commander.port) global.opts.core.server.port = parseInt(commander.port); if (commander.hostname) global.opts.core.server.hostname = commander.hostname; if (!commander.watch) { trackStats.event({ group: 'features', event: 'disabled watch' }); global.opts.core.watch.enabled = false; } /* /Globals */ /* App config */ // Version app.use(function (req, res, next) { res.header('X-powered-by', 'SourceJS ' + global.engineVersion); next(); }); // Optimization app.use(require('compression')()); // Cookies app.use(require('cookie-parser')()); app.use(require('express-session')({ secret: (function() { var d = new Date().getTime(); return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); })(), resave: false, saveUninitialized: true })); app.use(function (req, res, next) { if (req.cookies) { req.cookies['source-mode'] = global.MODE; } res.cookie('source-mode', global.MODE, { maxAge: 3600000, httpOnly: false }); next(); }); var shortid = require('shortid'); app.use(function (req, res, next) { if (req.cookies && !req.cookies['source-track']) { var id = shortid.generate(); req.cookies['source-track'] = id; res.cookie('source-track', id, { maxAge: 3600000, httpOnly: true }); } next(); }); // Favicon var faviconPath = path.join(app.get('user'), 'favicon.ico'); if (fs.existsSync(faviconPath)){ app.use(favicon(faviconPath)); } app.use(bodyParser.json()); /* /App config */ /* Includes */ // Middlewares require('./core/middlewares/loader').process(app, global.opts); // Auth initializing var auth = require('./core/auth')(app); app.use(auth.everyauth.middleware()); // File tree module var fileTree = require('./core/file-tree'); // Run file tree scan on start fileTree.scan(); // Run file tree scan on main page visit if (global.opts.core.fileTree.mainPageTrigger && global.MODE !== 'presentation') { app.use(function(req, res, next){ // Updating navigation on each main page visit if (req.url === '/') fileTree.scan(); next(); }); } // Update file tree via api app.use('/api/updateFileTree', function(req, res){ fileTree.scan(); res.jsonp({ message: 'Navigation succesfully updated.' }); }); // Routes require('./core/routes'); // API require('./core/api'); require('./core/api/optionsApi'); // User extenstions require("./core/loadPlugins.js"); try { // User additional functionality require(app.get('user') + "/core/app.js"); } catch(e){} // Watchers if (global.opts.core.watch.enabled && global.MODE === 'development') { if (global.opts.core.watch.foreverWatchEnabled) { require('./core/watch'); } else { require('./core/watch/childWatch'); } } /* /Includes */ /* Serving content */ var headerFooter = require('./core/headerFooter'); // Static content app.use(express.static(app.get('user'))); // Page 404 app.use(function(req, res){ if (req.accepts('html')) { if (req.url === '/') { res.redirect('/docs'); return; } var headerFooterHTML = headerFooter.getHeaderAndFooter(); res.status(404).render(path.join(__dirname, '/core/views/404.ejs'), { header: headerFooterHTML.header, footer: headerFooterHTML.footer }); } }); /* /Serving content */ /* Error handling */ var logErrors = function(err, req, res, next) { if (err) { var url = req.url || ''; log.debug(req.method, req.headers); log.error(('Requested url: ' + url).red, ('Error: ' + err.stack).red); if (req.xhr) { res.status(500).json({msg: 'Server error'}); } else { res.status(500).send('Server error'); } } else { next(); } }; app.use(logErrors); /* /Error handling */ /* Server start */ if (!module.parent) { var serverOpts = global.opts.core.server; var port = serverOpts.port; app.listen(port, serverOpts.hostname, serverOpts.backlog, serverOpts.callback); log.info('[SOURCEJS] launched on http://127.0.0.1:'.blue + (port.toString()).red + ' in '.blue + MODE.blue + ' mode...'.blue); if (commander.test) { var spawn = require('cross-spawn'); spawn('./node_modules/grunt-cli/bin/grunt', [commander.postGrunt, '--port='+port], {stdio: 'inherit'}) .on('close', function (code) { if (code === 0) { log.info('Test successful'); process.exit(0); } else { log.error('Test failed'); process.exit(1); } }); } else { if (global.opts.core.tracking.enabled) { trackStats.event({ group: 'start', event: 'default' }); } else { trackStats.event({ group: 'start', event: 'disabled tracking' }, true); } } } /* Server start */ ================================================ FILE: appveyor.yml ================================================ # AppVeyor file # http://www.appveyor.com/docs/appveyor-yml environment: matrix: - nodejs_version: 0.10 - nodejs_version: 0.12 install: - ps: Install-Product node $env:nodejs_version - npm install build: off before_test: "git clone https://github.com/sourcejs/init.git user" test_script: - node --version && npm --version - ps: "npm run ci-test-win # PowerShell" # Pass comment to PS for easier debugging - cmd: "npm run ci-test-win" # Run without watch because of CI bug matrix: fast_finish: true cache: - C:\Users\appveyor\AppData\Roaming\npm\node_modules -> package.json # global npm modules - C:\Users\appveyor\AppData\Roaming\npm-cache -> package.json # npm cache - node_modules -> package.json # local npm modules ================================================ FILE: assets/css/base/buttons.less ================================================ /* Buttons ---------------------------------------------------------------------------------- */ /* TODO: cosmetic changes needed */ .source_btn { position:relative; display: inline-block; padding: 4px 17px; background-color:#65A7D6; border: #5B9ECB solid 1px; border-radius:3px; color:#fff; font-weight:normal; text-decoration:none; font-size: 12px; &:hover { background-color: #5B9ECB; } &:active { top:1px; } } /* /Buttons ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/base/grids.less ================================================ /* Grids ---------------------------------------------------------------------------------- */ .source_col-main { margin: 0 auto; padding: 0 @layout_col-main--padding; max-width: @layout_col-main--max-width; box-sizing: content-box; } .source_col { display: inline-block; vertical-align: top; width: 33%; padding-right: 25px; box-sizing: border-box; &.__15 { width: 20%; } &.__14 { width: 24.5%; } &__23 { width: 66.2%; } &.__right { float: right; padding-right: 0; } } /* Justified layout -------------------------------------------------- */ /* justified inline data */ .jcol-l, .jcol-r { display: inline-block; width: 50%; vertical-align: middle; text-align: left; } .jcol-r { text-align: right; } /* /Justified layout */ /* /Grids ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/base/navigation.less ================================================ /* Navigation ---------------------------------------------------------------------------------- */ .source_nav { white-space: nowrap; display: none; font-family: @font-family-main; color: @color-main; &.__loaded { display: block; } &:empty { display: none; } } @media all and (min-width: 990px) { .source_nav:before { content: ''; display: inline-block; vertical-align: middle; height: 100%; } } .source_nav_c { display: inline-block; vertical-align: top; border-radius: 10px; width: 100%; box-sizing: border-box; padding: 20px 21px 10px; background-color: @light-gray; } .source_nav_ul { list-style: none; margin: 0; padding: 0; li:first-child { margin-top: 0; } a { display: block; line-height: inherit; } } .source_main_nav_ul2 { margin: 0; } /* /Navigation ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/base/ntf.less ================================================ .source_ntf { position: fixed; z-index: 50; top: 50px; /* toolbar height */ left: 0; right: 0; font-size: @fz-size-m; font-family: @font-family-main; color: #333; text-align: center; height: 50px; line-height: 48px; white-space: nowrap; overflow: hidden; background: #FFF6DD; border-bottom: 1px solid #ccc; box-shadow: 0 0 6px -2px rgba(0, 0, 0, .3); opacity: 0; visibility: hidden; transition: opacity .2s, visibility .2s; } .source_ntf.__active { opacity: 1; visibility: visible; } ================================================ FILE: assets/css/base/togglers.less ================================================ /* Togglers ---------------------------------------------------------------------------------- */ .source_slider_frame { position: absolute; right: 0; top: 0; display: block; margin: 0 auto; width: 50px; height: 20px; background-color: rgb(246, 249, 251); border-radius: 4px; box-shadow: inset 0px 0px 4px 0 rgba(0, 0, 0, 0.25); -webkit-touch-callout: none; user-select: none; } .source_slider_button { display: block; width: 30px; height: 20px; line-height: 20px; background: #EDF2F7; border-radius: 4px; transition: all 0.25s ease-in-out; color: #000; font-family: sans-serif; font-size: 9px; text-align: center; cursor: pointer; box-shadow: inset 0px 0px 4px 0 rgba(0, 0, 0, 0.25); } .source_slider_frame__on .source_slider_button { margin-left: 20px; background: #EDF2F7; } .source_slider_frame .source_slider_text_on { display: none; } .source_slider_frame__on .source_slider_text_off { display: none; } .source_slider_frame__on .source_slider_text_on { display: block; } .source_slider_frame__on .source_slider_button { background: #73c051; color: #fff; } /* /Togglers ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/clarify.bundle.less ================================================ /* * * Clarify styles * */ //Based on rhr.me/MCSS methodology @import url('variables.less'); @import url('mixins.less'); @import url('reset.less'); //Project @import url('project/clarify.less'); ================================================ FILE: assets/css/cosmetic/helpers.less ================================================ /* Helpers ---------------------------------------------------------------------------------- */ .source_delim { background: none; border: 0 none; border-top: 1px dotted @black; height: 0; padding: 0; margin: 20px 0; } .source_clear { width: 100%; clear: both; height: 0; font-size:0; line-height:0; } .source_tx { margin: 5px 0 20px; } .source_huge { font-size: 1.2em; } .source_ul { margin: 0; padding: 0; margin-left: 18px; list-style: outside disc; } .source_ul li { margin: 5px 0; } /* /Helpers ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/cosmetic/highlights.less ================================================ /* Highlights ---------------------------------------------------------------------------------- */ .source_code { background: @light-gray; font-family: Menlo, Monaco, Consolas, monospace; font-size: .85em; padding: 0 3px; border-radius: 3px; border: 1px solid #dfe2e4; white-space: normal; color: #07A; } .source-only(code,{ .source_code; }); .source-main-only(code,{ .source_code; }); .lang-source_wide-code { padding: 10px !important; font-size: @fz-size-xs !important; display: inline-block; } .source_hl { background: #ceeeff; font-family: @font-family-main; font-size: 1em; padding: 1px 3px 2px; border: none; border-radius: 3px; color: @black; } /* /Highlights ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/cosmetic/links.less ================================================ /* Links ---------------------------------------------------------------------------------- */ // common link mixin .source-link(@color, @hover, @active : lighten(@hover, 10%)) { color: @color; &:link, &:visited { text-decoration: none; } &:hover { color: @hover; text-decoration: underline; } &:active { color: @active; } &:focus { outline: 2px solid @color-primary; outline-offset: 2px; } } // primary link / action link .source-a_a { .source-link(@color-primary, lighten(@color-primary, 10%)); } // secondary link / object link .source-a_o { .source-link(@color-secondary, lighten(@color-secondary, 10%)); } /* TODO: rename classes */ .source_a_hl, /* Higlight link */ .source_a_o { /* Regular link */ &:link, &:visited { text-decoration:none; color: lighten(@blue, 10%); } &:hover { color: @blue; text-decoration: underline; } } .source_a_bl { /* Emphasized link */ &:link, &:visited { color: @black; } } .source_a_l { /* Inverted link */ &:link, &:visited { color: lighten(@black, 40%); text-decoration: none; line-height: 1.5; } &:hover { color: #fff; } } .source_a_g { /* Object link */ &:link, &:visited { color: @blue; text-decoration: none; } &:hover { color: @blue; text-decoration: underline; } } .source_a_hl { /* Higlight link */ font-size: 16px !important; line-height: 2; &:after { content:"\A"; white-space:pre; } } .source_a_d { /* Design link */ &:link, &:visited { color: @green !important; font-size: 16px; } &:hover { color: @green; text-decoration: underline; } &:before { content: ''; display: inline; padding-right: 25px; background: url(/source/assets/i/spec_design_link.svg) 50% no-repeat; } } .source_a_s { /* Spec link */ &:link, &:visited { color: @green !important; font-size: 16px; } &:hover { color: @green; text-decoration: underline; } &:before { content: ''; display: inline; padding-right: 25px; background: url(/source/assets/i/spec_link.svg) 50% no-repeat; } } .source_a_fs-m { /* medium font-size */ &:link, &:visited { font-size: @fz-size-m; } } /* /Links ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/defaults.less ================================================ /* * * Core styles * * ! Modify with caution or use /user/assets/css/defaults.css to override core styles ! * */ //Based on rhr.me/MCSS methodology @import url('variables.less'); @import url('mixins.less'); @import url('reset.less'); //Base @import url('base/togglers.less'); @import url('base/buttons.less'); @import url('base/navigation.less'); @import url('base/grids.less'); @import url('base/ntf.less'); //Project @import url('project/layout.less'); @import url('project/header.less'); @import url('project/footer.less'); @import url('project/catalog.less'); @import url('project/navigation.less'); @import url('project/info.less'); @import url('project/section.less'); @import url('project/examples.less'); @import url('project/source-code.less'); @import url('project/search.less'); @import url('project/modal.less'); @import url('project/autocomplete.less'); @import url('project/clarify-in-spec.less'); @import url('project/auth.less'); //Cosmetic @import url('cosmetic/highlights.less'); @import url('cosmetic/links.less'); @import url('cosmetic/helpers.less'); /*# sourceMappingURL=defaults.css.map */ ================================================ FILE: assets/css/mixins.less ================================================ /* Mixins and functions ---------------------------------------------------------------------------------- */ /** * Mixin for applying styles everywhere except .source_example * * @Params: @selector - String (div, a, etc) * @rules - css properties ({border: 1px; color: red;}) * @reset - Boolean enable of disable reset (enabled by default) [true | false] */ .source-only(@selector, @rules, @reset: true) { .source_section > @{selector}@{notCleaningClasses}:not(.source_section_h):not(h2):not(h3):not(h4):not(.source_note):not(.source_data):not(.source_doc):not(.source_warn):not(.source_info), .source_section > *@{notCleaningClasses} > @{selector}, .source_section > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_section > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_section > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_info > @{selector}, .source_info > *@{notCleaningClasses} > @{selector}, .source_info > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_info > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_info > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_note > @{selector}, .source_note > *@{notCleaningClasses} > @{selector}, .source_note > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_note > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_note > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_doc > @{selector}, .source_doc > *@{notCleaningClasses} > @{selector}, .source_doc > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_doc > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_doc > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_warn > @{selector}, .source_warn > *@{notCleaningClasses} > @{selector}, .source_warn > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_warn > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_warn > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_deps > @{selector}, .source_deps > *@{notCleaningClasses} > @{selector}, .source_deps > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_deps > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_deps > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector} { & when (@reset = true) { .reset(); } @rules(); } } .source-main-only(@selector, @rules, @reset: true) { .source_main > @{selector}, .source_main > *@{notCleaningClasses} > @{selector}, .source_main > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_main > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector}, .source_main > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > *@{notCleaningClasses} > @{selector} { & when (@reset = true) { .reset(); } @rules(); } } /* /Mixins and functions ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/auth.less ================================================ .source_login { display: inline-block; height: 40px; float: right; margin-top: 2px; vertical-align: top; } .source_login-avatar { width: 25px; height: 25px; border-radius: 50%; border: 1px solid #FFF; cursor: pointer; } .source_login-avatar.anonymous { border: 1px solid #333; } .source_login-button { display: inline-block; color: #999; text-decoration: none; line-height: 1; cursor: pointer; vertical-align: top; margin: 5px 0px 0px 10px; } .source_login-button:hover { color: #FFF } ================================================ FILE: assets/css/project/autocomplete.less ================================================ /* Autocomplete ---------------------------------------------------------------------------------- */ .autocomplete-wrapper { border: 1px solid #ccc; border-radius: 4px; background: #FFF; font-size: 12px; font-family: @font-family-main; line-height: 1.5; } .autocomplete-suggestions { overflow: auto; margin-top: 1px; } .autocomplete-suggestions strong { font-weight: normal; text-shadow: 0 0 1px @light-gray; color: #00A0FE; } .autocomplete-suggestions span { color: @gray; } .autocomplete-suggestion { padding: 4px 8px; padding-left: 28px; white-space: nowrap; overflow: hidden; max-width: 100%; text-overflow: ellipsis; cursor: pointer; } .autocomplete-suggestion > a { color: @black; text-decoration: none; } .autocomplete-selected { background: @light-gray; } .autocomplete-selected:before, .autocomplete-show-all:hover:before, .autocomplete-show-all.__active:before { content: "►"; color: @gray; margin: 0 6px 0 -18px; } .autocomplete-show-all { background: @light-gray; } .autocomplete-show-all:hover, .autocomplete-show-all.__active { background: #d1d2d3; } /* /Autocomplete ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/catalog.less ================================================ /* Catalog ---------------------------------------------------------------------------------- */ .source_section .source_catalog { padding: 0 20px 20px; border:1px solid #dfe2e4; border-radius:10px; } .source_catalog { position: relative; margin-bottom: 20px; font-family: @font-family-main; font-size: @fz-size-m; line-height: 1.5; color: @gray !important; &_h2, h2 { line-height: 1.5 !important; font-size: 22px !important; color: @black !important; margin: 20px 0 5px 0 !important; font-weight: normal !important; a { color: @black; text-decoration: underline; &:hover { text-decoration: none; } } } &.__show-preview { .source_catalog_img { display: block; } .source_catalog_list_i { margin-bottom: 30px; } } } .source_catalog_title { line-height: 1.5; a { text-decoration: underline; font-size: inherit; &:hover { color: @blue; } } } .source_catalog_a:hover { text-decoration: none !important; .source_catalog_title { color: #fff; } } .source_catalog > p, .source_catalog_tx { margin: 0 0 20px !important; color: @gray !important; font-size: @fz-size-s !important; &.__2l { /* 2 line */ min-height: 36px; } p { margin: 0 0 10px; } } .source_subhead > p, .source_subhead .source_catalog_tx { margin: 0 0 10px !important; min-height: 0; } .source_catalog_list { list-style: none; margin: 0; padding: 0; } .source_catalog_list_i { width: 30%; margin-right: 3% !important; display: inline-block; vertical-align: top; } .source_catalog.__show-preview .source_catalog_list_i { vertical-align: baseline; } @media (min-width: 1200px) { .source_catalog_list_i { width: 22%; margin-right: 3% !important; } } .source_col .source_catalog_list_i { /* Context */ width: 100%; display: block; } .source_section .source_catalog_list_i { width: 47% !important; } .source_catalog_a { position: relative; display: block; margin-left: -10px; padding: 5px 10px; font-size: @fz-size-m; text-decoration: none; a&:hover { //tag for weight text-decoration: none; background-color: @blue__hover !important; color: #fff; border-radius: 5px; .source_catalog_footer { color: #fff; } } } .source_catalog_update-button, .source_catalog_image-tumbler { // Reset background: none; border: none; padding: 0; display: inline; cursor: pointer; font-size: @fz-size-s; text-decoration: none; border-bottom: 1px dotted; color: #808080; &:hover { color: #303030; text-decoration: none; } } .source_catalog_img { display: none; max-width: 100%; max-height: 60px; margin-bottom: 5px; border-radius: 2px; } .source_catalog_footer { color: @gray; font-size: 11px; line-height: 1.5; text-decoration: none !important; } .source_catalog_all { margin: 20px 0 0 0; a.source_a_bl { color: @gray; text-decoration: underline; &:hover { color: @black; } } } .source_catalog-filter { float: right; margin-top: 10px; width: 24.5%; } .source_sort-list { list-style-type: none; margin: 0; padding: 0; color: #808080; } .source_sort-list_li { display: inline-block; line-height: 1.5; font-size: @fz-size-s; } .source_sort-list_a { font-size: @fz-size-s; text-decoration: none; border-bottom: 1px dotted; color: #808080; &:hover { color: #303030; text-decoration: none; } } .source_sort-list_li.__active { color: #303030; &:after { content: "\25B2"; font-size: 11px; display: inline-block; padding-left: 3px; } &.__forward:after { content: "\25BC"; } .source_sort-list_a { color: #303030; } } /* /Catalog ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/clarify-in-spec.less ================================================ .source_clarify-in-spec_link { display: block !important; width: 18px; height: 18px; background: url("/source/assets/i/link-external.svg") 50% no-repeat !important; background-size: contain !important; float: right !important; margin-right: -15px !important; margin-top: 5px !important; } .source_section > h3 > .source_clarify-in-spec_link { width: 27px; margin-top: 3px !important; margin-right: -19px !important; background-color: #fff !important; z-index: 1 !important; position: relative !important; } ================================================ FILE: assets/css/project/clarify.less ================================================ /* Context -------------------------------------------------- */ .source_clarify { } .source_clarify .source_section_h { margin-bottom: 10px; margin-top: 20px; } .source_clarify.__clear .source_clarify_content { padding: 0; } .source_clarify.__clear .source_clarify_panel { margin: 20px 0 0 0; } /* /Context */ /* Layout -------------------------------------------------- */ .source_clarify_content { padding: 0 20px 20px; } /* /Layout */ /* Panel -------------------------------------------------- */ .source_clarify_panel { .reset(); width: 608px; max-width: calc(100% - 40px); box-sizing: border-box; border-radius: 10px; margin: 0 20px 20px; padding: 20px 21px 10px; background-color: #f1f2f3; font-family: @font-family-main; font-size: @fz-size-m; line-height: 1.5; } .source_clarify_panel_i { margin-bottom: 15px; } .source_clarify_panel_i label { .reset(); } .source_clarify_panel_h2 { .reset(); font-family: @font-family-main; font-size: @fz-size-m; color: #333333; margin: 0 0 5px; line-height: 1.5; text-rendering: auto; } .source_clarify_panel_sections { width: 100%; min-height: 120px; border-color: #dfe2e4; padding: 5px; } .source_clarify_panel_sections > option { .reset(); font-family: @font-family-main; font-size: @fz-size-xs; line-height: 1.5; } .source_clarify_panel_option-list, .source_clarify_panel_option-list li { .reset(); font-family: @font-family-main; font-size: @fz-size-s; line-height: 1.5; } .source_clarify_panel_go { font-size: 14px; padding: 7px; & + .source_clarify_panel_return-link { display: inline-block; margin-left: 10px; } } .source_clarify_panel_return-link { .reset(); text-decoration: underline; &:hover { text-decoration: none; } } /* /Panel */ ================================================ FILE: assets/css/project/examples.less ================================================ /* Code examples ---------------------------------------------------------------------------------- */ .source_example { position: relative; display: inline-block; vertical-align: top; padding: 20px; min-width: 500px; border: 5px solid #f0f0f0; margin: 10px 0 15px; background: none; } .source_example:hover { z-index: 20; } .source_example:empty { display: none; } .source_example__2 { width: 215px; } .source_example__3 { width: 730px; } .source_example__4 { width: 980px; } .source_example__5 { width: 1200px; } .source_example__auto { width: auto; } .source_example__full { width: 100%; box-sizing: border-box; } .source_example__2, .source_example__3, .source_example__4, .source_example__5, .source_example__auto, .source_example__full { min-width: 0; } .source_example__3, .source_example__4, .source_example__5, .source_example__full { margin-right: 0; } /* /Code examples ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/footer.less ================================================ /* Footer ---------------------------------------------------------------------------------- */ .source_footer { position: relative; z-index: 11; /* Higher than nav */ margin: 0 -25px; -ms-flex-preferred-size: auto; border-top: 1px solid #eee; padding: 30px 0; background: @black; box-sizing: border-box; font-family: @font-family-main; font-size: @fz-size-xs; line-height: 1.5; color: @gray; } .source_footer h2 { .reset(); margin-bottom: 10px; font-weight: normal; font-size: 15px; color: #666; } .source_footer .source_ul { color: @gray; margin: 0; list-style: none; } .source_footer_copy { margin-top: 40px; line-height: 2; } .source_footer_version { color: @color-aux2; } /* /Footer ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/header.less ================================================ /* Header ---------------------------------------------------------------------------------- */ .source_header { position: relative; margin: -50px -25px 0; height: 50px; min-width: 990px; padding-top: 11px; padding-bottom: 10px; z-index: 9999; /* overlap all */ top: 0; left: 0; right: 0; background: @black; box-sizing: border-box; transition: opacity .2s; white-space: nowrap; font-family: @font-family-main; font-size: @fz-size-m; @media @media_min-step-m { position: fixed; margin: 0; } .source__scrolled-down & { opacity: 1; @media @media_min-step-m {opacity: .1;} } .source__scrolled-down &:hover { opacity: 1; } } .source__scrolled-down .source_header__focus, .source_header__focus { opacity: 1; } .source_header .source_logo { display: inline-block; vertical-align: baseline; margin-right: 25px; line-height: 30px !important; color: #67a9d4; font-size: 24px !important; font-family: 'Museo', Arial; text-decoration: none; &:hover { color: #67a9d4; text-decoration: underline; } } /* Navigation -------------------------------------------------- */ .source_header_nav { display: inline-block; vertical-align: 1px; } .source_header_nav_lst { margin: 0; padding: 0; list-style: none; } .source_header_nav_i { display: inline-block; margin-left: 20px; /* Reset */ line-height: 1.5; &:first-child { margin-left: 0; } } /* /Navigation */ /* /Header ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/info.less ================================================ /* Info blocks ---------------------------------------------------------------------------------- */ // !important next to rules are intended solution till we will use shadow DOM or iframes for source_examples .source_data, .source_doc, .source_note, .source_warn, .source_info { display: block; position: relative; box-sizing: content-box !important; margin-top: 20px !important; margin-bottom: 20px !important; line-height: 1.5; border-radius: @border-radius !important; @media @media_min-step-m { // Legacy margin-right: @layout_spec--inner-margin-right !important; // New spec page modifier .source_page.__spec & { margin-right: 0 !important;} } .source_section & { margin-right: 0 !important; } .source_section & { font-size: @fz-size-s !important; } &:empty { display: none; } p { margin: 0 0 10px !important; &:last-child { margin-bottom: 0 !important; } } th, td { border-color: #868686 !important; } > *:last-child { margin-bottom: 0 !important; } } .source_info { background: #FFF6DD !important; padding: 15px 25px !important; min-height: 50px !important; margin-top: 15px !important; margin-bottom: 15px !important; } .source_warn { padding: 10px 10px 10px 35px !important; background: #ffe2e5 url(/source/assets/i/warn.png) no-repeat 13px 13px !important; .source_section & { margin-right: 0 !important; } } .source_note { padding: 10px 10px 10px 35px !important; background: #DCF1FA url(/source/assets/i/note.png) no-repeat 13px 13px !important; } .source_doc { padding: 10px 25px !important; background: #DCFCDA !important; font-family: Menlo, Monaco, Consolas, monospace !important; & code, & p { font-family: Menlo, Monaco, Consolas, monospace !important; } } .source_data { padding: 10px 25px !important; border: 1px dotted #ccc !important; background: #f9f9f9 !important; font-size: 11px !important; } /* /Info blocks ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/layout.less ================================================ /* Layout ---------------------------------------------------------------------------------- */ .source_container { position: relative; padding: 50px 25px 0; /*350px; /* Footer height + margin */ min-height: 100vh; overflow-x: hidden; width: 100%; display: flex; flex-direction: column; box-sizing: border-box; .opera12 & { display: block; } @media @media_min-step-m { min-width: 990px; } } .source_main { position: relative; padding: 35px 0 120px; margin-bottom: 40px; flex: 1; -ms-flex: none; width: 100%; box-sizing: border-box; counter-reset: h2; @media @media_min-step-m { .source_page.__spec & { padding-right: @layout_spec--inner-margin-right; } } &.__loading:after { content: ''; position: absolute; width: 100%; height: 90px; left: 0; bottom: 15px; background-image: url(data:image/gif;base64,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); background-repeat: no-repeat; background-position: 50% 0; } } .source_main_h1 { margin: 0; padding: 10px 0 0 0; font-size: 36px; font-weight: normal; font-family: 'Museo', Arial; /* reset */ line-height: 1.5; color: @black; } .source_header-meta { font-size: @fz-size-xs !important; line-height: 1.2 !important; color: @color-aux; margin-bottom: 20px; } .source-main-only(h1,{ .source_main_h1; }); /* for custom cases */ .source_subhead { position: relative; margin: -35px -100% 20px; padding: 0 100% 10px; background-color: #f2f2f2; font-family: @font-family-main; font-size: @fz-size-m; color: @gray; line-height: 1.5; // Clearfix &:before, &:after { content: " "; /* 1 */ display: table; /* 2 */ } &:after { clear: both; } } .source_main_descr > ul, .source_main_descr > p, .source_main_descr { margin-bottom: 20px; font-size: @fz-size-m; } /* /Layout ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/modal.less ================================================ /* Modalbox ---------------------------------------------------------------------------------- */ .source_modal_box { background: #fff; } .source_modal_title { font-size: 22px; margin: 20px 0 5px 0; } .source_modal_close { position: absolute; top: 10px; right: 10px; color: #fff; border-radius: 30px; background: #605F61; font-size: 20px !important; display: inline-block; line-height: 0px !important; padding: 9px 3px; cursor: pointer; } .source_modal_close:before { content: "×"; } /* /Modalbox ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/navigation.less ================================================ /* Navigation ---------------------------------------------------------------------------------- */ .source_main_nav { margin-top: 40px; @media @media_min-step-m { position: fixed; z-index: 10; top: 170px; //@source_main_nav--top; right: @layout_col-main--padding; bottom: 40px; width: @source_main_nav--width; margin-top: 0; //reset margin-right: 0; //reset transition: top 0.3s; .source__scrolled-down & { top: 70px; } } @media @media_min-step-l { position: fixed; z-index: 10; left: 50%; top: @source_main_nav--top; width: @source_main_nav--width; margin-left: @layout_col-main--max-width / 2 - @source_main_nav--width; margin-top: 0; //reset margin-right: 0; //reset } } .source_main_nav:hover { z-index: 20; /* Higher than any other elements */ } .source_main_nav h2 { font-weight: normal; font-size: 15px; color: @black; margin: 0 0 5px; line-height: 1.5; font-family: inherit; text-rendering: auto; } .source_main_nav.__menuScroll { overflow-x: hidden; overflow-y: auto; bottom: 15px; } .source_main_nav_i { margin: 10px 0; font-size: @fz-size-xs; } .source_main_nav_i:first-child { margin-top: 0; } .source_main_nav_i + .source_main_nav_i { padding-top: 10px; border-top: 1px solid #dfe2e4; } .source_main_nav_ul { margin-left: -21px; /* menu padding compensation */ margin-right: -21px; } .source_main_nav_li { line-height: 20px; margin-bottom: 2px; } .source_main_nav_ul2 { padding: 0; } .source_main_nav_li2 { color: #666; overflow: hidden; display: none; text-overflow: ellipsis; white-space: nowrap; } .source_main_nav_li.__active .source_main_nav_li2 { display: block; } .source_nav_ul .source_main_nav_a { overflow: hidden; text-overflow: ellipsis; text-decoration: none; padding: 0 21px; color: #4792D2; &:hover { color: #1c76c2; text-decoration: underline; } } .source_main_nav_li2 .source_main_nav_a { padding: 0 37px; } .source_main_nav .source_main_nav_a.__active { /* for weight */ background-color: #8AB5D6; color: #F9FBFD; } .source_main_nav_ac_item { position: relative; line-height: 24px; &.__simple { text-align: right; } } .source_main_nav_ac_tx { line-height: 24px; } .source_main_nav_ac_item.__simple { text-align: right; white-space: normal; } .source_main_nav_ac_item.__simple .source_main_nav_ac_tx { float: left; } /* /Navigation ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/search.less ================================================ /* Search ---------------------------------------------------------------------------------- */ .source_search { display: inline-block; overflow: hidden; height: 28px; width: 100%; border: 1px solid #444; border-radius: 5px; vertical-align: top; } .source_search.__light { border-color: #ccc; } .source_search.__light input.source_search_it { /* Cascade for weight */ background-color: #FFF; } .source_container input.source_search_it { /* Cascade for weight */ padding: 6px 8px; padding-left: 28px; height: 30px; margin: -1px 0 0 -1px; width: 101%; border: 0 none; line-height: 15px; background: #000 url(/source/assets/i/search.png) no-repeat 8px 50%; box-sizing: border-box; color: @gray; /* Resets */ display: block; box-shadow: none; border-radius: 0; transition: none; font-weight: 400; font-family: @font-family-main; font-size: @fz-size-m; line-height: 1.5; vertical-align: middle; } .source_container input.source_search_it:focus { background: #fff url(/source/assets/i/search.png) no-repeat 8px 50%; color: @black; } /* /Search ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/section.less ================================================ /* Section ---------------------------------------------------------------------------------- */ .source_section { padding-left: @layout_spec--inner-padding; counter-reset: h3; @media @media_min-step-m { // Legacy margin-right: @layout_spec--inner-margin-right; // New spec page modifier .source_page.__spec & { margin-right: 0; } } & > h2:first-child { margin-left: -@layout_spec--inner-padding !important; &:before { counter-increment: h2 1; content: counter(h2)". "; color: #666; } } /* Error detection */ & > h2:not(:first-child) { .reset(); background-color: rgba(250, 0, 0, .2); outline: 3px solid red; outline-offset: 3px; &:after { content: " error - duplicate h2 or not first tag in section"; font-weight: normal; color: @black; } } & > h3 { position: relative; margin: 30px 0 15px -25px; padding: 0 25px; font-size: 16px; color: #000; font-weight: normal; line-height: 1.5; font-family: inherit; counter-reset: h4; &:after { content: ""; border-bottom: 1px dotted #666; position: absolute; left: 0; right: 0; top: 50%; } } & > h3 > span { position: relative; z-index: 1; display: inline-block; padding: 0 5px; background: #fff; &:before { color: @gray; counter-increment: h3 1; content: counter(h2)"."counter(h3)". "; } } & > h4 { margin: 30px 0 10px; padding: 0; font-size: @fz-size-m; font-family: @font-family-main; color: @black; font-weight: normal; text-indent: .5ex; line-height: 1.5; &:before { color: @gray; counter-increment: h4 1; content: counter(h2)"."counter(h3)"."counter(h4)". "; } } & > p { margin: 15px 0; } & > pre { .reset(); } } /* Dynamics -------------------------------------------------- */ /* Everything closed by default */ .source_section { position: absolute; height: 0; width: 0; top: -9999px; overflow: hidden; &.__loaded { position: relative; height: auto; width: auto; overflow: visible; top: 0; margin-top: 20px; margin-bottom: 40px; } & > script, & > style { display: none !important; } body & > * { //body for weihgt display: none; } body & > h2:first-child { //body for weihgt display: block; } } .source_section__open { & > *:not(.source_example) { display: block; } & > .source_a_hl { display: inline; } .source_example { display: inline-block; } .spoiler-cont_css_w, .spoiler-cont_js_w, .spoiler-cont_xml_w { display: none !important; } .source_section_h_expand:after { content: "▼"; } .source_source-code__show, .source_source-code__static, .source_context.__show { display: block !important; } .source_context { display: none !important; } } /* Header actions */ .source_section > h2:first-child, .source_section_h { .reset(); position: relative; margin: 0 0 25px; padding: 5px @layout_spec--inner-padding; line-height: 28px; color: @black; border-radius: 10px; background-color: @light-gray; font-size: @fz-size-l; font-family: @font-family-main; &:hover .source_section_h_expand { color:#4792D2 !important; //important to override link style } } .source_subsection_h { background-color: #fff !important; padding: 0 5px !important; } .source_section_h_expand { position: absolute; top: 0; left: -20px; width: 20px; height: 38px; margin: 0 10px 0 0; font-size: 12px !important; //override reset styles text-decoration: none; color: @gray !important; //important to override link style &:after { content: "►"; position: absolute; top: 0; left: 0; width: 100%; text-align: left; line-height: 38px; } } /* /Header actions */ /* /Dynamics */ /* /Section ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/project/source-code.less ================================================ /* Code highlighting ---------------------------------------------------------------------------------- */ .source_source-code, code.brush { display: none !important; /* Hide all code source by default */ } .source_source-code { font-size: @fz-size-m; } .source_source-code .source_source-code_toggle .source_show {display: none;} /* Default maximazed */ .source_source-code__min .source_source-code_toggle .source_hide {display: none;} /* minimized */ .source_source-code__min .source_source-code_toggle .source_show {display: inline;} .source_source-code__min .source_source-code_cnt { display: none; } .source_source-code_toggle { display: inline-block; height: 14px; margin-bottom: 10px; border-bottom: #25588D dashed 1px !important; font-size: 10px !important; text-decoration: none; & span { color: inherit !important; } } .source_source-code_toggle:hover { border-bottom: none !important; text-decoration: none !important; padding-bottom: 1px; } .source_source-code_toggle__css .source_show:after, .source_source-code_toggle__css .source_hide:after { content: " CSS"; } .source_source-code_toggle__html .source_show:after, .source_source-code_toggle__html .source_hide:after { content: " HTML"; } .source_source-code_toggle__js .source_show:after, .source_source-code_toggle__js .source_hide:after { content: " JavaScript"; } .source_source-code_toggle-all .source_hide {display: none;} /* Default hidden */ .source_source-code_toggle-all__hide .source_show {display: none;} .source_source-code_toggle-all__hide .source_hide {display: inline;} /* /Code highlighting ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/reset.less ================================================ /* Reset ---------------------------------------------------------------------------------- */ .reset() { margin: 0; padding: 0; border: 0; border-radius: 0; font-size: 100%; font-family: @font-family-main; font-weight: normal; vertical-align: baseline; background: none; outline: none; text-align: left; text-decoration: none; z-index: 1; line-height: 1; list-style: none; quotes: none; content: none; border-collapse: collapse; border-spacing: 0; box-sizing: border-box; box-shadow: none; color: #333; } /* Fonts -------------------------------------------------- */ /* * Web Fonts from fontspring.com * * All OpenType features and all extended glyphs have been removed. * Fully installable fonts can be purchased at http://www.fontspring.com * * The fonts included in this stylesheet are subject to the End User License you purchased * from Fontspring. The fonts are protected under domestic and international trademark and * copyright law. You are prohibited from modifying, reverse engineering, duplicating, or * distributing this font software. * * (c) 2010-2014 Fontspring * * * * * The fonts included are copyrighted by the vendor listed below. * * Vendor: exljbris Font Foundry * License URL: http://www.fontspring.com/licenses/exljbris/webfont * * */ @media @media_min-step-m { /* A font by Jos Buivenga (exljbris) -> www.exljbris.com */ @font-face { font-family: 'Museo'; src: url('/source/assets/fonts/MuseoCyrl_500-webfont.woff2') format('woff2'), url('/source/assets/fonts/MuseoCyrl_500-webfont.woff') format('woff'); font-weight: normal; font-style: normal; } @font-face { font-family: 'Museo Sans'; src: url('/source/assets/fonts/MuseoSansCyrl_500-webfont.woff2') format('woff2'), url('/source/assets/fonts/MuseoSansCyrl_500-webfont.woff') format('woff'); font-weight: normal; font-style: normal; } } /* /Fonts */ /* Spec page resets -------------------------------------------------- */ html, body { margin: 0; padding: 0; min-width: none; max-width: none; } @{cleaningClasses} { background: none; } .source-only(a,{ color: #4792D2; text-decoration: none; font-size: @fz-size-m; line-height: 1.5; &:hover { color: #1c76c2; text-decoration: underline; } }); .source-only(div,{ line-height: 1.5; }); .source-only(p,{ font-family: @font-family-main; font-size: @fz-size-m; line-height: 1.5; margin: 15px 0; }); .source-only(ul,{ margin: 25px 0 25px 22px; }); .source-only(ul ul,{ margin-top: 0 !important; }); .source-only(li,{ font-size: @fz-size-m; line-height: 1.5; list-style: disc inside; }); .source-only(ol,{ margin: 25px 0 25px 22px; }); .source-only(ol li,{ font-size: @fz-size-m; line-height: 1.5; list-style: decimal inside !important; }); .source-only(ol ol,{ margin-top: 0 !important; }); .source-only(ul ol,{ margin-top: 0 !important; }); .source-only(ol ul,{ margin-top: 0 !important; }); .source-only(h2,{}); .source-only(h3,{}); .source-only(h4,{}); .source-only(h5,{}); .source-only(h6,{}); .source-only(span,{}); .source-only(label,{}); .source-only(q,{}); .source-only(blockquote,{}); .source-only(strong,{ font-weight: 700; }); .source-only(b,{ font-weight: 700; }); .source-only(em,{}); .source-only(i,{}); .source-only(section,{}); .source-only(table,{ margin: 20px 0 30px; border-collapse: collapse; border-spacing: 0; font-size: @fz-size-m; font-family: @font-family-main; line-height: 1.5; }); .source-only(td,{ padding: 0; }); .source-only(th,{ padding: 6px 13px; border: 1px solid #ddd; font-weight: 700; }); .source-only(td,{ padding: 6px 13px; border: 1px solid #ddd; }); .source-only(tr,{ padding: 6px 13px; border: 1px solid #ddd; }); .source-only(nav,{}); .source-only(aside,{}); .source_main > .source_main_nav, .source_main > .source_deps, .source_main > .source_warn, .source_main > .source_info, .source_main > .source_doc, .source_main > p:not(.source_section), .source_main > section:not(.source_section), .source_main > div:not(.source_section), .source_main > a:not(.source_section) { font-family: @font-family-main; font-size: @fz-size-m; line-height: 1.5; } .source_header a, .source_footer a, .source_main_nav a, .source_main > p > a{ .reset(); } /* /Spec page resets */ /* /Reset ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/css/source.css ================================================ /*! * SourceJS - Living Style Guides Engine and Integrated Maintenance Environment for Front-end Components * @copyright 2013-2015 SourceJS.com * @license MIT license: http://github.com/sourcejs/source/wiki/MIT-License * */ /* Default core styles */ @import url('/source/assets/css/defaults.css'); /* Users override */ @import url('/assets/css/defaults.css'); ================================================ FILE: assets/css/variables.less ================================================ /* Variables ---------------------------------------------------------------------------------- */ @max-width: 1400px; /* Media -------------------------------------------------- */ //Mobile first @media_min-step-m: ~'all and (min-width: 990px)'; @media_min-step-l: ~'all and (min-width: 1400px)'; /* /Media */ /* Layout -------------------------------------------------- */ @border-radius: 10px; @layout_col-main--padding: 25px; @layout_col-main--max-width: @max-width - @layout_col-main--padding * 2; @layout_spec--inner-padding: 25px; @layout_spec--inner-margin-right: 32%; /* /Layout */ /* Navigation -------------------------------------------------- */ @source_main_nav--width: 280px; @source_main_nav--top: 163px; /* /Navigation */ /* Colors -------------------------------------------------- */ // primary @color-primary : #4593D5; @blue : @color-primary; @color-highlight : #7EB6D9; @blue__hover: @color-highlight; // secondary @color-secondary : #1b9b19; @green: @color-secondary; // main @color-main : #333; @black: @color-main; // aux @color-aux : #999; @gray: @color-aux; // aux level2 @color-aux2 : #696969; @dark-gray: @color-aux2; // utility @color-utility: #f1f2f3; @light-gray: @color-utility; /* /Colors */ /* Fonts -------------------------------------------------- */ @font-family-main : Arial, Helvetica, sans-serif; /* /Fonts */ /* Sizes -------------------------------------------------- */ @fz-size-l : 16px; @fz-size-m : 15px; @fz-size-s : 14px; @fz-size-xs : 13px; /* /Fonts */ /* Extras -------------------------------------------------- */ @cleaningClasses: ~'.source_example, .source_clean'; @notCleaningClasses: ~':not(.source_example):not(.source_clean):not(.dataTables_wrapper):not(.source_status-controls):not(.source_catalog):not(.source_source-code_cnt)'; // TODO: generate automatically /* /Extras */ /* /Variables ---------------------------------------------------------------------------------- */ ================================================ FILE: assets/fonts/Webfont EULA 1.6.txt ================================================ The Fontspring Webfont End User License Agreement Version 1.6.0 - March 13, 2014 By downloading, installing and/or embedding font software (“Webfont”) offered by Fontspring or its distributors, you (“Licensee”) agree to be bound by the following terms and conditions of this End User Licensing Agreement (“EULA”): 1. Right Granted Fontspring grants Licensee a perpetual, worldwide, non-exclusive and non-transferrable license to link the Webfont to Websites using the @font-face selector in CSS files. 2. Requirements and Restrictions Licensee agrees to abide by the following requirements and restrictions: a. Licensee must use the Webfont provided by Fontspring under this EULA. Licensee may not link to the full, CFF OpenType or TrueType font designed for desktop installation. b. Licensee must include the entire commented header in the provided CSS file. c. The total traffic of the Website(s), measured in pageviews per month, may be no greater than the number of pageviews specified in the Receipt. d. Licensee may only install the Webfont on Websites that it owns or controls. e. Licensee may embed Webfont in reports generated by the Website(s), provided that Licensee does not sell the reports for profit. 3. Provision to Third Parties Licensee may temporarily provide the Webfont to a website developer, agent or independent contractor, who is working on behalf of the Licensee, ONLY IF the developer, agent or independent contractor (1) agrees in writing to use the Font exclusively for Licensee’s work, according to the terms of this EULA, and (2) retains no copies of the Font upon completion of the work. Licensee may not otherwise distribute the Webfont to third parties or make the Webfont publicly accessible or available except by embedding or linking in accordance with this EULA. 4. Term This EULA grants a perpetual license for the rights set forth in Paragraph 1 unless and until the EULA terminates under Paragraph 8. Fontspring will not charge additional fees post purchase, annually or otherwise. 5. Other Usage Licenses for desktop use, computer applications and games, installable interactive books, software, mobile applications and games, Ebooks and Epubs, product creation websites, website template distribution, website templates, and other uses not allowed by this EULA may be available for an additional fee. Contact Fontspring at support@fontspring.com for more information. 6. Modifications Licensee may not modify the Webfont or create derivative works based upon the Webfont without prior written consent from Fontspring or the owning foundry EXCEPT THAT Licensee may generate files necessary for embedding or linking in accordance with this EULA. 7. Copyright The Webfont is protected by copyright law. The Foundry is the sole, exclusive owner of all intellectual property rights, including rights under copyright and trademark law. Licensee agrees not to use the Webfont in any manner that infringes the intellectual property rights of the Foundry or violates the terms of this EULA. Licensee will be held legally responsible, and indemnifies Fontspring, for any infringements on the foundry's rights caused by failure to abide by the terms of this EULA. 8. Termination This EULA is effective until terminated. If Licensee fails to comply with any term of this EULA, Fontspring may terminate the EULA with 30 days notice. This EULA will terminate automatically 30 days after the issuance of such notice. 9. Disclaimer and Limited Warranty Fontspring warrants the Product to be free from defects in materials and workmanship under normal use for a period of twenty one (21) days from the date of delivery as shown on Receipt. Fontspring's entire liability, and Licensee’s exclusive remedy, for a defective product shall be, at Fontspring's election, either (1) return of purchase price or (2) replacement of any such product that is returned to Fontspring with a copy of the Receipt. Fontspring shall have no responsibility to replace the product or refund the purchase price if failure results from accident, abuse or misapplication, or if any product is lost or damaged due to theft, fire, or negligence. Any replacement product will be warranted for twenty one (21) days. This warranty gives Licensee specific legal rights. Licensee may have other rights, which vary from state to state. EXCEPT AS EXPRESSLY PROVIDED ABOVE, THE PRODUCT, IS PROVIDED “AS IS”. FONTSPRING MAKES NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The entire risk as to the quality and performance of the Product rests upon Licensee. Neither Fontspring nor the Foundry warrants that the functions contained in the Product will meet Licensee’s requirements or that the operation of the software will be uninterrupted or error free. FONTSPRING SHALL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, OR INCIDENTAL DAMAGES (INCLUDING DAMAGES FROM LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, AND THE LIKE) ARISING OUT OF THE USE OF OR INABILITY TO USE THE PRODUCT EVEN IF FONTSPRING OR THE FOUNDRY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to Licensee. 10. Governing Law This EULA is governed by the laws of the United States of America and the State of Delaware. 11. Entire Agreement This EULA, in conjunction with the receipt (“Receipt”) that accompanies each Font licensed from Fontspring or its distributors, constitutes the entire agreement between Fontspring and Licensee. 12. Modification The Parties may modify or amend this EULA in writing. 13. Waiver. The waiver of one breach or default hereunder shall not constitute the waiver of any subsequent breach or default. ================================================ FILE: assets/js/_require.bundle.js ================================================ "{%= grunt.file.read('assets/js/lib/require.js') %}" "{%= grunt.file.read('assets/js/lib/jquery-2.1.4.min.js') %}" "{%= grunt.file.read('assets/js/require-config.js') %}" // Extending base js config with npm packages list requirejs.config({ // Create shorthands routes to clint-side npm plugins packages: function () { var modulesList = "{%= npmPluginsEnabled %}"; var npmPackages = []; for (var module in modulesList) { npmPackages.push({ name: module, location: '/node_modules/' + module + '/assets', main: 'index' }) } return npmPackages; }() }); ================================================ FILE: assets/js/clarify.js ================================================ /*! * SourceJS - Front-end documentation engine * @copyright 2013-2015 Sourcejs.com * @license MIT license: http://github.com/sourcejs/source/wiki/MIT-License * */ require([ "jquery", 'text!/api/options', "sourceModules/utils", "sourceLib/lodash", "text!sourceTemplates/clarifyPanel.inc.html" ], function ($, options, u, _, clarifyPanelTpl){ // If we have data from Clarify output if (window.sourceClarifyData){ var _options = JSON.parse(options); var htmlParser = _options.plugins && _options.plugins.htmlParser && _options.plugins.htmlParser.enabled; var $panelTemplate = $(_.template(clarifyPanelTpl, { htmlParser: htmlParser, showApiTargetOption: window.sourceClarifyData.showApiTargetOption, specUrl: window.sourceClarifyData.specUrl, tplList: window.sourceClarifyData.tplList, sectionsIDList: window.sourceClarifyData.sectionsIDList || [] })); var enableCheckboxes = function(param){ if (u.getUrlParameter(param)) { $panelTemplate.find('.js-source_clarify_panel_option-checkbox[name="'+param+'"]').attr('checked', true); } }; // Restoring options from URL var checkboxes = ['nojs','fromApi','apiUpdate']; checkboxes.forEach(function(item){ enableCheckboxes(item); }); var template = u.getUrlParameter('tpl') ? u.getUrlParameter('tpl') : 'default'; $panelTemplate.find('.js-source_clarify_panel_select-tpl').val(template); var sections = u.getUrlParameter('sections') ? u.getUrlParameter('sections').split(',') : undefined; if (sections) { sections.forEach(function(item){ $panelTemplate.find('.js-source_clarify_panel_sections > option[data-section="' + item + '"]').attr('selected', true); }); } // Import template $('.js-source_clarify_panel').replaceWith($panelTemplate); // Activating changes $('.js-source_clarify_panel_go').on('click', function(e){ e.preventDefault(); var currentUrl = window.location.href.split('?')[0]; var clarifyBaseUrl = currentUrl + '?clarify=true'; var constructedParams = ''; $('.js-source_clarify_panel_option-checkbox').each(function(){ var t = $(this); if (t.is(':checked')){ constructedParams += '&' + t.attr('name') + '=true' } }); var selectedTpl = $('.js-source_clarify_panel_select-tpl').val(); if (selectedTpl !== 'default'){ constructedParams += '&tpl=' + selectedTpl; } var selectedSections = []; $('.js-source_clarify_panel_sections > option:selected').each(function(){ var t = $(this); selectedSections.push(t.attr('data-section')); }); if (selectedSections.length > 0){ constructedParams += '§ions=' + selectedSections.join(','); } location.href = clarifyBaseUrl + constructedParams; }); } else { console.log('Clarify panel failed to receive expected data from clarify, check your tpl.'); } }); ================================================ FILE: assets/js/enter-the-source.js ================================================ /*! * SourceJS - Front-end documentation engine * @copyright 2013-2015 Sourcejs.com * @license MIT license: http://github.com/sourcejs/source/wiki/MIT-License * */ require([ "jquery", "source/load-options", // TODO: remove when all modules inherit Module() "sourceModules/browser", "sourceModules/moduleLoader", 'sourceModules/auth' ], function ($, options, browser, Loader, Auth) { if (options && options.modulesEnabled && options.modulesEnabled.auth === true) { new Auth({ target: $('.js-hook.source_login') }); } }); ================================================ FILE: assets/js/lib/autocomplete.js ================================================ "use strict"; (function(module) { if (typeof(define) === "function" && define.amd) { define(["jquery"], module); } else { module(jquery); } })(function($) { var Autocomplete = function(target, options) { if (!target) return; this.init(target, options); }; var keys = { "ESC": 27, "TAB": 9, "RETURN": 13, "UP": 38, "DOWN": 40, "CTRL": 17, "CMD": 91 }; var transliteration = { 'ЗГ':'ZGH', 'Зг':'Zgh', 'зг':'zgh', 'А':'A', 'а':'a', 'Б':'B', 'б':'b', 'В':'V', 'в':'v', 'Г':'G', 'г':'g', 'Д':'D', 'д':'d', 'Е':'E', 'е':'e', 'Ж':'Zh', 'ж':'zh', 'З':'Z', 'з':'z', 'И':'I', 'и':'i', 'ї':'i', 'Й':'I', 'й':'i', 'К':'K', 'к':'k', 'Л':'L', 'л':'l', 'М':'M', 'м':'m', 'Н':'N', 'н':'n', 'О':'O', 'о':'o', 'П':'P', 'п':'p', 'Р':'R', 'р':'r', 'С':'S', 'с':'s', 'Т':'T', 'т':'t', 'У':'U', 'у':'u', 'Ф':'F', 'ф':'f', 'Х':'Kh', 'х':'kh', 'Ц':'Ts', 'ц':'ts', 'Ч':'Ch', 'ч':'ch', 'Ш':'Sh', 'ш':'sh', 'Щ':'Shch', 'щ':'shch', 'Ы':'Y', 'ы':'y', 'Э':'E', 'э':'e', 'Ю':'Yu', 'ю':'iu', 'Я':'Ya', 'я':'ia', 'Ь': '`', 'ь': '`', 'Ъ': '`', 'ъ': '`' }; Autocomplete.prototype = { config : { "lookup": [], "transliteration": true, "autoSelectFirst": true, "caseSensetive": false, "classes": { "container": "autocomplete-suggestions", "selected": "autocomplete-selected", "suggestion": "autocomplete-suggestion", "wrapper": "autocomplete-wrapper", "showAll": "autocomplete-show-all autocomplete-suggestion", "active": "__active" }, "containerParent": "body", "containerHeight": 500, // px "showAll": undefined, // e.g. function(data) { } "labels": { "showAllButtonText": "Show all" }, "suggestionsLimit": 0 }, init: function(target, options) { this.$target = $(target); this.visible = false; this.resultsCount = 0; this.cachedSearchResults = this.cachedSearchResults || {}; $.extend(true, this.config, options); this.initContainer(); this.initHandlers(); }, /** * @function initContainer. It initializes search results box, * its position and size. */ initContainer: function() { var config = this.config; var $target = this.$target; var $container = this.$container = $('
').addClass(config.classes.container); var rootElement = $container; var containerHeight = this.config.containerHeight; if (typeof(config.showAll) === "function") { var showAllLabel = this.resultsCount > 0 ? config.labels.showAllButtonText + ' (' + this.resultsCount +')' : config.labels.showAllButtonText; var $showAll = this.$showAll = $("
") .addClass(config.classes.showAll) .html("" + showAllLabel + ""); var $wrapper = $("
").addClass(config.classes.wrapper) .append($container) .append($showAll); rootElement = $wrapper; } this.$root = rootElement.css({ "position": "fixed", "display": "none", "z-index": 9999 }); rootElement.appendTo(config.containerParent); var _this = this; var relocateContainer = function() { var offset = _this.$target.position(); var bottomOffset = 75; // px var headerPadding var wHeight = $(window).height(); rootElement.css({ "top": (offset.top + _this.$target.outerHeight(true)) + "px", "left": offset.left + "px" }); $container.css("max-height", (wHeight < containerHeight ? wHeight - bottomOffset : containerHeight) + "px"); var width = $target.outerWidth(true) - 2; rootElement.width(width); }; $(window).resize(relocateContainer); relocateContainer(); }, /** * @function initHandlers. It initializes handlers for search items & container events * * N.B. * To implement new handler you need to add it in "handlers" object below * To use implemented event handler you can recieve it by getHandler() method. * * JFYI: Implemented handlers are binded (by jQuery.proxy) to Autocomplete instance, * due to that, "this" in handlers body is a pointer to Autocomplete instance. */ initHandlers: function() { var _this = this; this.keyMap = []; // this array helps to catch keys combination. this.$target.on(window.opera ? "keypress" : "keydown", this.getHandler("onKeyPress")) .on('keyup', this.getHandler("onKeyUp")) .on('input', this.getHandler("onValueChanged")); this.$target.on("blur", function(e) { $(document).one("click", function(e) { if (!$(e.target).is(_this.$target) && !$.contains(_this.$container.get(0), e.target)) { if ($(e.target).is(_this.$showAll) || $.contains(_this.$showAll.get(0), e.target)) { _this.visible = false; _this.$root.hide(); _this.flushSelection(); } else { _this.flush(); } } }); }); var containerSelector = "." + this.config.classes.container; var itemSelector = "." + this.config.classes.suggestion; var callback = this.config.onSelect; this.$container.on("mouseenter", itemSelector, function() { _this.select($(this).data("index")); }); this.$container.on("mouseleave", itemSelector, function() { _this.selectedIndex = -1; _this.select(); }); if (this.$showAll) { this.$showAll.on('click', this.getHandler("onShowAllClick")); } }, getHandler: function(name) { return $.proxy(handlers[name], this); }, createResultRow: function(index, item) { return $("
") .addClass(this.config.classes.suggestion) .data("index", index) .html('' + item.value + '').get(0); }, wrapItems: function(data) { var result = document.createDocumentFragment(); var _this = this; var limit = this.config.suggestionsLimit; var max = limit > 0 && limit < data.length ? limit : data.length; for (var i = 0; i < max; i++) { result.appendChild(_this.createResultRow(i, data[i])); } return result; }, getSearchQuery: function() { var inputText = this.$target.val(); if (!inputText || !inputText.length) { return; } var isTranslitEnabled = this.config.transliteration; var transliterated = inputText; // TODO: make sure that we realy need it inputText = inputText.replace(/[\{\}\[\]\(\)\\\.\*\?\+]{1}/g, ""); inputText = this.config.caseSensetive ? inputText : inputText.toLowerCase(); var cyrillic = /[\u0400-\u04FF]/gi; if (isTranslitEnabled) { if (transliterated.search(cyrillic) >= 0) { transliterated = transliterated.replace(cyrillic, function(ch) { return transliteration[ch]; }); } else { $.each(transliteration, function(cyr, lat) { transliterated = transliterated.replace(new RegExp(lat,["g"]), cyr); }); } } return isTranslitEnabled ? transliterated + "|" + inputText : inputText; }, getSearchResults: function(searchQuery) { var data = []; var caseSensetive = this.config.caseSensetive; var replacementExpr = "$1"; $.map(this.config.lookup, function(item) { var pattern = caseSensetive ? item.value : item.value.toLowerCase(); var substrStartPos = pattern.search(new RegExp(searchQuery)); if (substrStartPos < 0) return true; data.push({ "value": item.value.replace(new RegExp("(" + searchQuery + ")",'gi'), replacementExpr), "data": item.data }); }); if (data.length && searchQuery) { this.cachedSearchResults[searchQuery] = data; } return data; }, formatSearchResult: function() { var searchQuery = this.getSearchQuery(); if (!searchQuery || !searchQuery.length) { this.flush(); } else { var cachedResult = this.cachedSearchResults[searchQuery]; var searchResult = cachedResult && cachedResult.length ? cachedResult : this.getSearchResults(searchQuery); this.resultsCount = searchResult.length; this.refreshItemsCount(); return searchResult; } }, flush: function() { this.visible = false; this.$target.val(""); this.$container.empty() this.$root.hide(); this.flushSelection(); }, refreshItemsCount: function() { var showAllLabel = this.resultsCount > 0 ? this.config.labels.showAllButtonText + ' (' + this.resultsCount +')' : this.config.labels.showAllButtonText; this.$showAll.find('a').html(showAllLabel); }, /** * selection handlers for search results */ selectNext: function() { var $container = this.$container; var itemsLength = $container.children().length; if ((this.selectedIndex < 0) || (this.selectedIndex + 1 >= itemsLength)) { this.$showAll.addClass(this.config.classes.active); var selectedClass = this.config.classes.selected; $container.children("." + selectedClass).removeClass(selectedClass); return; } this.selectedIndex += 1; var newItem = $(this.$container.children().get(this.selectedIndex)); var outerHeight = newItem.outerHeight(); var viewPortBottom = $container.scrollTop() + $container.height(); var itemBottom = (1 + this.selectedIndex) * outerHeight; if (viewPortBottom <= itemBottom) { $container.scrollTop($container.scrollTop() + outerHeight); } this.select(); }, selectPrev: function() { var $container = this.$container; var itemsLength = $container.children().length; if (!this.$showAll.hasClass(this.config.classes.active)) { this.selectedIndex = this.selectedIndex < 0 ? 0 : this.selectedIndex === 0 ? 0 : this.selectedIndex - 1; } var newItem = $(this.$container.children().get(this.selectedIndex)); var viewPortTop = $container.scrollTop(); var outerHeight = newItem.outerHeight(); var itemTop = (this.selectedIndex) * outerHeight; if (viewPortTop >= itemTop) { $container.scrollTop($container.scrollTop() - outerHeight); } this.select(); }, select: function(index) { this.$showAll.removeClass(this.config.classes.active); var selectionIndex = this.selectedIndex = index >= 0 ? index : this.selectedIndex; var $container = this.$container; var selectedClass = this.config.classes.selected; if (selectionIndex < 0) { $container.children("." + selectedClass).removeClass(selectedClass); return; } var items = this.$container.children(); if (!items || !items.length) return; setTimeout(function() { $container.children("." + selectedClass).removeClass(selectedClass); $(items.get(selectionIndex)).addClass(selectedClass); }, 1); }, flushSelection: function() { this.selectedIndex = this.config.autoSelectFirst ? 0 : -1; this.select(); }, openSelected: function(inNewTab) { var selectedItem = this.$container.children().get(this.selectedIndex); if (!selectedItem) return; var link = $(selectedItem).find("a").attr("href"); if (!link) return; // we should check if origin exists (in case of IE) var winLocation = window.location; var isOriginExists = winLocation && winLocation.origin; var url = isOriginExists ? winLocation.origin + link : winLocation.protocol + "//" + winLocation.hostname + (winLocation.port ? ":" + winLocation.port : "") + link; window.open(url, inNewTab ? "_blank" : "_self"); window.focus(); } }; // We use setTimeout here to prevent huge number of handlers calls var searchQueryTimeout; Autocomplete.prototype.getSearchData = function() { if (!searchQueryTimeout) { var _this = this; searchQueryTimeout = setTimeout(function() { var dataSubset = _this.formatSearchResult(); if (dataSubset && dataSubset.length) { _this.$container.html(_this.wrapItems(dataSubset)); _this.$root.show(); _this.visible = true; } else { _this.visible = false; _this.$container.empty(); _this.$root.hide(); } searchQueryTimeout = 0; _this.flushSelection(); }, 50); } }; // this for handlers is Autocomplete object instance var handlers = { "onKeyPress": function(e) { e = e || event; //to make a deal with old IE this.keyMap.push(e.keyCode); switch(e.keyCode) { case (keys.ESC) : this.flush(); break; case (keys.UP): this.selectPrev(); break; case (keys.DOWN): this.selectNext(); break; case (keys.TAB): this.selectNext(); break; case (keys.RETURN): break; default: return; } }, "onKeyUp": function(e) { e = e || event; // to make a deal with IE if (~$.inArray(keys.RETURN, this.keyMap)) { if (this.$showAll.hasClass(this.config.classes.active)) { this.$showAll.trigger("click"); this.visible = false; this.$root.hide(); this.flushSelection(); this.keyMap = []; return; } var isModifierPressed = ~$.inArray(keys.CTRL, this.keyMap) || ~$.inArray(keys.CMD, this.keyMap); this.openSelected(isModifierPressed); } this.keyMap = []; }, "onValueChanged": function(e) { this.getSearchData(); }, "onShowAllClick": function(e) { var callback = this.config.showAll; if (typeof(callback) !== "function") return false; callback.call(this, this.formatSearchResult()); } }; $.fn.autocomplete = function(options, args) { return this.each(function () { var searchInstance = new Autocomplete(this, options); }); }; }); ================================================ FILE: assets/js/lib/codeFormat.js ================================================ /* * * HTML Code Formatter * * @author Dennis Przendzinski * * */ define([ "jquery" ], function($) { return function($el, options) { var selfClosing = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]; // list of self-closing tags return $el.each(function() { var settings = $.extend({ escape:true // escape HTML symbols (<,>,&) ,log:false // console log source code }, options) , tabs = 0 , code = $(this).html() ; var indentCode = function (line) { var _tabs = tabs < 0 ? 0 : tabs; return new Array(_tabs + 1).join(' ') + line; }; if (code.length > 0) { code = code.trim() .replace(/[\n\t\r]/g, '') // strip all tabs, carriage returns and new lines .replace(/\s{2,}/g, ' ') // strip extra (2+) white spaces .replace(/< /g, "<") // remove spaces after "<" .replace(new RegExp('<(\\b(' + selfClosing.join("|") + ')\\b[^>]*?)>', "g"), '<$1/>') // create regex from inline array or self-closing tags and close them if required .replace(/<(?!\/)/g, "\n<") // start each tag from new line .match(/<(?!\/)[^>].+?\/>||<(?!\/)[^>]+?>.*?<\/[^>]+?>.*?(?=<)|<(?!\/)[^>]+?>*?<\/[^>]+?>|<(?!\/)[^>]+?>.+?<\/[^>]+?>|<(?!\/)[^>]+>.*|<(?=\/)[^>].+?>|[^<\r\n]+/g) // combine into array ; for (var i=0; i< code.length; i++) { if (code[i].match(/<(?!\/)/) && !(code[i].match(/<(?=\/)/)) && !(code[i].match(//))) { // if the string contains opening tag and doesn't contain closing tag... var tag = code[i].match(/<([^ \/>]+)/)[1]; // ...strip the tag ( -> img) to check against the array of self-closing tags and increase indentation code[i] = indentCode(code[i]); if (selfClosing.indexOf(tag) === -1) { tabs +=2; } } else if (!(code[i].match(/<(?!\/)/)) && code[i].match(/<(?=\/)/)) { // if the string contains closing tag and doesn't contain opening tag, decrease indentation tabs -=2; code[i] = indentCode(code[i]); } else { // otherwise just keep current indentation code[i] = indentCode(code[i]); } if (settings.escape) { code[i] = code[i].replace(/&/g, "&").replace(//g,">"); } if (settings.log) { console.log(code[i]); } } $(this).html(code.join('\n')); } }); } }); ================================================ FILE: assets/js/lib/html5shiv.js ================================================ /*! HTML5 Shiv vpre3.6 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */ (function(g,i){var l=g.html5||{};var c=/^<|^(?:button|form|map|select|textarea|object|iframe|option|optgroup)$/i;var j=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i;var b;var d;(function(){var m=i.createElement("a");m.innerHTML="";b=("hidden" in m);if(b&&typeof injectElementWithStyles=="function"){injectElementWithStyles("#modernizr{}",function(n){n.hidden=true;b=(g.getComputedStyle?getComputedStyle(n,null):n.currentStyle).display=="none"})}d=m.childNodes.length==1||(function(){try{(i.createElement)("a")}catch(n){return true}var o=i.createDocumentFragment();return(typeof o.cloneNode=="undefined"||typeof o.createDocumentFragment=="undefined"||typeof o.createElement=="undefined")}())}());function k(m,o){var q=m.createElement("p"),n=m.getElementsByTagName("head")[0]||m.documentElement;q.innerHTML="x";return n.insertBefore(q.lastChild,n.firstChild)}function f(){var m=e.elements;return typeof m=="string"?m.split(" "):m}function h(n){var m={},q=n.createElement,o=n.createDocumentFragment,p=o();n.createElement=function(s){if(!e.shivMethods){return q(s)}var r;if(m[s]){r=m[s].cloneNode()}else{if(j.test(s)){r=(m[s]=q(s)).cloneNode()}else{r=q(s)}}return r.canHaveChildren&&!c.test(s)?p.appendChild(r):r};n.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+f().join().replace(/\w+/g,function(r){q(r);p.createElement(r);return'c("'+r+'")'})+");return n}")(e,p)}function a(n){var m;if(n.documentShived){return n}if(e.shivCSS&&!b){m=!!k(n,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")}if(!d){m=!h(n)}if(m){n.documentShived=m}return n}var e={elements:l.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:!(l.shivCSS===false),shivMethods:!(l.shivMethods===false),type:"default",shivDocument:a};g.html5=e;a(i)}(this,document)); ================================================ FILE: assets/js/lib/jquery.autocomplete.js ================================================ /** * Ajax Autocomplete for jQuery, version 1.2.4 * (c) 2013 Tomas Kirda * * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. * For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ * */ /*jslint browser: true, white: true, plusplus: true */ /*global define, window, document, jQuery */ // Expose plugin as an AMD module if AMD loader is present: (function (factory) { 'use strict'; if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else { // Browser globals factory(jQuery); } }(function ($) { 'use strict'; var utils = (function () { return { extend: function (target, source) { return $.extend(target, source); }, addEvent: function (element, eventType, handler) { if (element.addEventListener) { element.addEventListener(eventType, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + eventType, handler); } else { throw new Error('Browser doesn\'t support addEventListener or attachEvent'); } }, removeEvent: function (element, eventType, handler) { if (element.removeEventListener) { element.removeEventListener(eventType, handler, false); } else if (element.detachEvent) { element.detachEvent('on' + eventType, handler); } }, createNode: function (html) { var div = document.createElement('div'); div.innerHTML = html; return div.firstChild; } }; }()), keys = { ESC: 27, TAB: 9, RETURN: 13, UP: 38, DOWN: 40 }; function Autocomplete(el, options) { var noop = function () { }, that = this, defaults = { autoSelectFirst: false, appendTo: 'body', serviceUrl: null, lookup: null, onSelect: null, width: 'auto', minChars: 1, maxHeight: 300, deferRequestBy: 0, params: {}, formatResult: Autocomplete.formatResult, delimiter: null, zIndex: 9999, type: 'GET', noCache: false, onSearchStart: noop, onSearchComplete: noop, containerClass: 'autocomplete-suggestions', tabDisabled: false, dataType: 'text', lookupFilter: function (suggestion, originalQuery, queryLowerCase) { return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; }, paramName: 'query', transformResult: function (response, originalQuery) { var result = typeof response === 'string' ? $.parseJSON(response) : response; result.query = originalQuery; return result; } }; // Shared variables: that.element = el; that.el = $(el); that.suggestions = []; that.badQueries = []; that.selectedIndex = -1; that.currentValue = that.element.value; that.intervalId = 0; that.cachedResponse = []; that.onChangeInterval = null; that.onChange = null; that.ignoreValueChange = false; that.isLocal = false; that.suggestionsContainer = null; that.options = $.extend({}, defaults, options); that.classes = { selected: 'autocomplete-selected', suggestion: 'autocomplete-suggestion' }; // Initialize and set options: that.initialize(); that.setOptions(options); } Autocomplete.utils = utils; $.Autocomplete = Autocomplete; Autocomplete.formatResult = function (suggestion, currentValue) { var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g'), pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')'; return suggestion.value.replace(new RegExp(pattern, 'gi'), '$1<\/strong>'); }; Autocomplete.prototype = { killerFn: null, initialize: function () { var that = this, suggestionSelector = '.' + that.classes.suggestion, selected = that.classes.selected, options = that.options, container; // Remove autocomplete attribute to prevent native suggestions: that.element.setAttribute('autocomplete', 'off'); that.killerFn = function (e) { if ($(e.target).closest('.' + that.options.containerClass).length === 0) { that.killSuggestions(); that.disableKillerFn(); } }; // Determine suggestions width: if (!options.width || options.width === 'auto') { options.width = that.el.outerWidth(true) - 2; } that.suggestionsContainer = Autocomplete.utils.createNode(''); container = $(that.suggestionsContainer); container.appendTo(options.appendTo).width(options.width); // Listen for mouse over event on suggestions list: container.on('mouseover', suggestionSelector, function () { that.activate($(this).data('index')); }); // Deselect active element when mouse leaves suggestions container: container.on('mouseout', function () { that.selectedIndex = -1; container.children('.' + selected).removeClass(selected); }); // Listen for click event on suggestions list: container.on('click', suggestionSelector, function () { that.select($(this).data('index'), false); }); that.fixPosition(); // Opera does not like keydown: if (window.opera) { that.el.on('keypress', function (e) { that.onKeyPress(e); }); } else { that.el.on('keydown', function (e) { that.onKeyPress(e); }); } that.el.on('keyup', function (e) { that.onKeyUp(e); }); that.el.on('blur', function () { that.onBlur(); }); that.el.on('focus', function () { that.fixPosition(); }); $(window).resize(function(){ that.fixPosition(); $(that.suggestionsContainer).css({ 'width': that.el.outerWidth(true) - 2 + 'px' }); }); }, onBlur: function () { this.enableKillerFn(); }, setOptions: function (suppliedOptions) { var that = this, options = that.options; utils.extend(options, suppliedOptions); that.isLocal = $.isArray(options.lookup); if (that.isLocal) { options.lookup = that.verifySuggestionsFormat(options.lookup); } // Adjust height, width and z-index: $(that.suggestionsContainer).css({ 'max-height': options.maxHeight + 'px', 'width': options.width + 'px', 'z-index': options.zIndex }); }, clearCache: function () { this.cachedResponse = []; this.badQueries = []; }, disable: function () { this.disabled = true; }, enable: function () { this.disabled = false; }, fixPosition: function () { var that = this, offset; // Don't adjsut position if custom container has been specified: if (that.options.appendTo !== 'body') { return; } offset = that.el.offset(); $(that.suggestionsContainer).css({ top: (offset.top + that.el.outerHeight(true)) + 'px', left: offset.left + 'px' }); }, enableKillerFn: function () { var that = this; $(document).on('click', that.killerFn); }, disableKillerFn: function () { var that = this; $(document).off('click', that.killerFn); }, killSuggestions: function () { var that = this; that.stopKillSuggestions(); that.intervalId = window.setInterval(function () { that.hide(); that.stopKillSuggestions(); }, 300); }, stopKillSuggestions: function () { window.clearInterval(this.intervalId); }, onKeyPress: function (e) { var that = this; // If suggestions are hidden and user presses arrow down, display suggestions: if (!that.disabled && !that.visible && e.keyCode === keys.DOWN && that.currentValue) { that.suggest(); return; } if (that.disabled || !that.visible) { return; } switch (e.keyCode) { case keys.ESC: that.el.val(that.currentValue); that.hide(); break; case keys.TAB: case keys.RETURN: if (that.selectedIndex === -1) { that.hide(); return; } that.select(that.selectedIndex, e.keyCode === keys.RETURN); if (e.keyCode === keys.TAB && this.options.tabDisabled === false) { return; } break; case keys.UP: that.moveUp(); break; case keys.DOWN: that.moveDown(); break; default: return; } // Cancel event if function did not return: e.stopImmediatePropagation(); e.preventDefault(); }, onKeyUp: function (e) { var that = this; if (that.disabled) { return; } switch (e.keyCode) { case keys.UP: case keys.DOWN: return; } clearInterval(that.onChangeInterval); if (that.currentValue !== that.el.val()) { if (that.options.deferRequestBy > 0) { // Defer lookup in case when value changes very quickly: that.onChangeInterval = setInterval(function () { that.onValueChange(); }, that.options.deferRequestBy); } else { that.onValueChange(); } } }, onValueChange: function () { var that = this, q; clearInterval(that.onChangeInterval); that.currentValue = that.element.value; q = that.getQuery(that.currentValue); that.selectedIndex = -1; if (that.ignoreValueChange) { that.ignoreValueChange = false; return; } if (q.length < that.options.minChars) { that.hide(); } else { that.getSuggestions(q); } }, getQuery: function (value) { var delimiter = this.options.delimiter, parts; if (!delimiter) { return $.trim(value); } parts = value.split(delimiter); return $.trim(parts[parts.length - 1]); }, getSuggestionsLocal: function (query) { var that = this, queryLowerCase = query.toLowerCase(), filter = that.options.lookupFilter; return { suggestions: $.grep(that.options.lookup, function (suggestion) { return filter(suggestion, query, queryLowerCase); }) }; }, getSuggestions: function (q) { var response, that = this, options = that.options; response = that.isLocal ? that.getSuggestionsLocal(q) : that.cachedResponse[q]; if (response && $.isArray(response.suggestions)) { that.suggestions = response.suggestions; that.suggest(); } else if (!that.isBadQuery(q)) { options.params[options.paramName] = q; options.onSearchStart.call(that.element, options.params); $.ajax({ url: options.serviceUrl, data: options.params, type: options.type, dataType: options.dataType }).done(function (data) { that.processResponse(data, q); options.onSearchComplete.call(that.element, q); }); } }, isBadQuery: function (q) { var badQueries = this.badQueries, i = badQueries.length; while (i--) { if (q.indexOf(badQueries[i]) === 0) { return true; } } return false; }, hide: function () { var that = this; that.visible = false; that.selectedIndex = -1; $(that.suggestionsContainer).hide(); }, suggest: function () { if (this.suggestions.length === 0) { this.hide(); return; } var that = this, formatResult = that.options.formatResult, value = that.getQuery(that.currentValue), className = that.classes.suggestion, classSelected = that.classes.selected, container = $(that.suggestionsContainer), html = ''; // Build suggestions inner HTML: $.each(that.suggestions, function (i, suggestion) { html += '
' + formatResult(suggestion, value) + '
'; }); container.html(html).show(); that.visible = true; // Select first value by default: if (that.options.autoSelectFirst) { that.selectedIndex = 0; container.children().first().addClass(classSelected); } }, verifySuggestionsFormat: function (suggestions) { // If suggestions is string array, convert them to supported format: if (suggestions.length && typeof suggestions[0] === 'string') { return $.map(suggestions, function (value) { return { value: value, data: null }; }); } return suggestions; }, processResponse: function (response, originalQuery) { var that = this, options = that.options, result = that.options.transformResult(response, originalQuery); result.suggestions = that.verifySuggestionsFormat(result.suggestions); // Cache results if cache is not disabled: if (!options.noCache) { that.cachedResponse[result[options.paramName]] = result; if (result.suggestions.length === 0) { that.badQueries.push(result[options.paramName]); } } // Display suggestions only if returned query matches current value: if (result.query === that.getQuery(that.currentValue)) { that.suggestions = result.suggestions; that.suggest(); } }, activate: function (index) { var that = this, activeItem, selected = that.classes.selected, container = $(that.suggestionsContainer), children = container.children(); container.children('.' + selected).removeClass(selected); that.selectedIndex = index; if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { activeItem = children.get(that.selectedIndex); $(activeItem).addClass(selected); return activeItem; } return null; }, select: function (i, shouldIgnoreNextValueChange) { var that = this, selectedValue = that.suggestions[i]; if (selectedValue) { that.el.val(selectedValue); that.ignoreValueChange = shouldIgnoreNextValueChange; that.hide(); that.onSelect(i); } }, moveUp: function () { var that = this; if (that.selectedIndex === -1) { return; } if (that.selectedIndex === 0) { $(that.suggestionsContainer).children().first().removeClass(that.classes.selected); that.selectedIndex = -1; that.el.val(that.currentValue); return; } that.adjustScroll(that.selectedIndex - 1); }, moveDown: function () { var that = this; if (that.selectedIndex === (that.suggestions.length - 1)) { return; } that.adjustScroll(that.selectedIndex + 1); }, adjustScroll: function (index) { var that = this, activeItem = that.activate(index), offsetTop, upperBound, lowerBound, heightDelta = 25; if (!activeItem) { return; } offsetTop = activeItem.offsetTop; upperBound = $(that.suggestionsContainer).scrollTop(); lowerBound = upperBound + that.options.maxHeight - heightDelta; if (offsetTop < upperBound) { $(that.suggestionsContainer).scrollTop(offsetTop); } else if (offsetTop > lowerBound) { $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); } that.el.val(that.getValue(that.suggestions[index].value)); }, onSelect: function (index) { var that = this, onSelectCallback = that.options.onSelect, suggestion = that.suggestions[index]; that.el.val(that.getValue(suggestion.value)); if ($.isFunction(onSelectCallback)) { onSelectCallback.call(that.element, suggestion); } }, getValue: function (value) { var that = this, delimiter = that.options.delimiter, currentValue, parts; if (!delimiter) { return value; } currentValue = that.currentValue; parts = currentValue.split(delimiter); if (parts.length === 1) { return value; } return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; } }; // Create chainable jQuery plugin: $.fn.autocomplete = function (options, args) { return this.each(function () { var dataKey = 'autocomplete', inputElement = $(this), instance; if (typeof options === 'string') { instance = inputElement.data(dataKey); if (typeof instance[options] === 'function') { instance[options](args); } } else { instance = new Autocomplete(this, options); inputElement.data(dataKey, instance); } }); }; })); ================================================ FILE: assets/js/lib/jquery.couch.js ================================================ // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. /** * @namespace * $.couch is used to communicate with a CouchDB server, the server methods can * be called directly without creating an instance. Typically all methods are * passed an options object which defines a success callback which * is called with the data returned from the http request to CouchDB, you can * find the other settings that can be used in the options object * from * jQuery.ajax settings *
$.couch.activeTasks({
 *   success: function (data) {
 *     console.log(data);
 *   }
 * });
* Outputs (for example): *
[
 *  {
 *   "pid" : "<0.11599.0>",
 *   "status" : "Copied 0 of 18369 changes (0%)",
 *   "task" : "recipes",
 *   "type" : "Database Compaction"
 *  }
 *]
*/ (function($) { $.couch = $.couch || {}; /** @lends $.couch */ /** * @private */ function encodeDocId(docID) { var parts = docID.split("/"); if (parts[0] == "_design") { parts.shift(); return "_design/" + encodeURIComponent(parts.join('/')); } return encodeURIComponent(docID); } /** * @private */ var uuidCache = []; $.extend($.couch, { urlPrefix: '', /** * You can obtain a list of active tasks by using the /_active_tasks URL. * The result is a JSON array of the currently running tasks, with each task * being described with a single object. * @see docs for /_active_tasks * @param {ajaxSettings} options jQuery ajax settings */ activeTasks: function(options) { ajax( {url: this.urlPrefix + "/_active_tasks"}, options, "Active task status could not be retrieved" ); }, /** * Returns a list of all the databases in the CouchDB instance * @see docs for /_all_dbs * @param {ajaxSettings} options jQuery ajax settings */ allDbs: function(options) { ajax( {url: this.urlPrefix + "/_all_dbs"}, options, "An error occurred retrieving the list of all databases" ); }, /** * View and edit the CouchDB configuration, called with just the options * parameter the entire config is returned, you can be more specific by * passing the section and option parameters, if you specify a value that * value will be stored in the configuration. * @see docs for /_config * @param {ajaxSettings} options * * jQuery ajax settings * @param {String} [section] the section of the config * @param {String} [option] the particular config option * @param {String} [value] value to be set */ config: function(options, section, option, value) { var req = {url: this.urlPrefix + "/_config/"}; if (section) { req.url += encodeURIComponent(section) + "/"; if (option) { req.url += encodeURIComponent(option); } } if (value === null) { req.type = "DELETE"; } else if (value !== undefined) { req.type = "PUT"; req.data = toJSON(value); req.contentType = "application/json"; req.processData = false } ajax(req, options, "An error occurred retrieving/updating the server configuration" ); }, /** * Returns the session information for the currently logged in user. * @param {ajaxSettings} options * * jQuery ajax settings */ session: function(options) { options = options || {}; ajax({ type: "GET", url: this.urlPrefix + "/_session", beforeSend: function(xhr) { xhr.setRequestHeader('Accept', 'application/json'); }, complete: function(req) { var resp = $.parseJSON(req.responseText); if (req.status == 200) { if (options.success) options.success(resp); } else if (options.error) { options.error(req.status, resp.error, resp.reason); } else { throw "An error occurred getting session info: " + resp.reason; } } }); }, /** * @private */ userDb : function(callback) { $.couch.session({ success : function(resp) { var userDb = $.couch.db(resp.info.authentication_db); callback(userDb); } }); }, /** * Create a new user on the CouchDB server, user_doc is an * object with a name field and other information you want * to store relating to that user, for example * {"name": "daleharvey"} * @param {Object} user_doc Users details * @param {String} password Users password * @param {ajaxSettings} options * * jQuery ajax settings */ signup: function(user_doc, password, options) { options = options || {}; user_doc.password = password; user_doc.roles = user_doc.roles || []; user_doc.type = user_doc.type = "user" || []; var user_prefix = "org.couchdb.user:"; user_doc._id = user_doc._id || user_prefix + user_doc.name; $.couch.userDb(function(db) { db.saveDoc(user_doc, options); }); }, /** * Authenticate against CouchDB, the options parameter is *expected to have name and password fields. * @param {ajaxSettings} options * * jQuery ajax settings */ login: function(options) { options = options || {}; $.ajax({ type: "POST", url: this.urlPrefix + "/_session", dataType: "json", data: {name: options.name, password: options.password}, beforeSend: function(xhr) { xhr.setRequestHeader('Accept', 'application/json'); }, complete: function(req) { var resp = $.parseJSON(req.responseText); if (req.status == 200) { if (options.success) options.success(resp); } else if (options.error) { options.error(req.status, resp.error, resp.reason); } else { throw 'An error occurred logging in: ' + resp.reason; } } }); }, /** * Delete your current CouchDB user session * @param {ajaxSettings} options * * jQuery ajax settings */ logout: function(options) { options = options || {}; $.ajax({ type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json", username : "_", password : "_", beforeSend: function(xhr) { xhr.setRequestHeader('Accept', 'application/json'); }, complete: function(req) { var resp = $.parseJSON(req.responseText); if (req.status == 200) { if (options.success) options.success(resp); } else if (options.error) { options.error(req.status, resp.error, resp.reason); } else { throw 'An error occurred logging out: ' + resp.reason; } } }); }, /** * @namespace * $.couch.db is used to communicate with a specific CouchDB database *
var $db = $.couch.db("mydatabase");
     *$db.allApps({
     *  success: function (data) {
     *    ... process data ...
     *  }
     *});
     * 
*/ db: function(name, db_opts) { db_opts = db_opts || {}; var rawDocs = {}; function maybeApplyVersion(doc) { if (doc._id && doc._rev && rawDocs[doc._id] && rawDocs[doc._id].rev == doc._rev) { // todo: can we use commonjs require here? if (typeof Base64 == "undefined") { throw 'Base64 support not found.'; } else { doc._attachments = doc._attachments || {}; doc._attachments["rev-"+doc._rev.split("-")[0]] = { content_type :"application/json", data : Base64.encode(rawDocs[doc._id].raw) }; return true; } } }; return /** @lends $.couch.db */{ name: name, uri: this.urlPrefix + "/" + encodeURIComponent(name) + "/", /** * Request compaction of the specified database. * @see docs for /db/_compact * @param {ajaxSettings} options * * jQuery ajax settings */ compact: function(options) { $.extend(options, {successStatus: 202}); ajax({ type: "POST", url: this.uri + "_compact", data: "", processData: false }, options, "The database could not be compacted" ); }, /** * Cleans up the cached view output on disk for a given view. * @see docs for /db/_compact * @param {ajaxSettings} options jQuery ajax settings */ viewCleanup: function(options) { $.extend(options, {successStatus: 202}); ajax({ type: "POST", url: this.uri + "_view_cleanup", data: "", processData: false }, options, "The views could not be cleaned up" ); }, /** * Compacts the view indexes associated with the specified design * document. You can use this in place of the full database compaction * if you know a specific set of view indexes have been affected by a * recent database change. * @see docs for /db/_compact/design-doc * @param {String} groupname Name of design-doc to compact * @param {ajaxSettings} options jQuery ajax settings */ compactView: function(groupname, options) { $.extend(options, {successStatus: 202}); ajax({ type: "POST", url: this.uri + "_compact/" + groupname, data: "", processData: false }, options, "The view could not be compacted" ); }, /** * Create a new database * @see docs for PUT /db/ * @param {ajaxSettings} options jQuery ajax settings */ create: function(options) { $.extend(options, {successStatus: 201}); ajax({ type: "PUT", url: this.uri, contentType: "application/json", data: "", processData: false }, options, "The database could not be created" ); }, /** * Deletes the specified database, and all the documents and * attachments contained within it. * @see docs for DELETE /db/ * @param {ajaxSettings} options jQuery ajax settings */ drop: function(options) { ajax( {type: "DELETE", url: this.uri}, options, "The database could not be deleted" ); }, /** * Gets information about the specified database. * @see docs for GET /db/ * @param {ajaxSettings} options jQuery ajax settings */ info: function(options) { ajax( {url: this.uri}, options, "Database information could not be retrieved" ); }, /** * @namespace * $.couch.db.changes provides an API for subscribing to the changes * feed *
var $changes = $.couch.db("mydatabase").changes();
         *$changes.onChange = function (data) {
         *    ... process data ...
         * }
         * $changes.stop();
         * 
*/ changes: function(since, options) { options = options || {}; // set up the promise object within a closure for this handler var timeout = 100, db = this, active = true, listeners = [], promise = /** @lends $.couch.db.changes */ { /** * Add a listener callback * @see docs for /db/_changes * @param {Function} fun Callback function to run when * notified of changes. */ onChange : function(fun) { listeners.push(fun); }, /** * Stop subscribing to the changes feed */ stop : function() { active = false; } }; // call each listener when there is a change function triggerListeners(resp) { $.each(listeners, function() { this(resp); }); }; // when there is a change, call any listeners, then check for // another change options.success = function(resp) { timeout = 100; if (active) { since = resp.last_seq; triggerListeners(resp); getChangesSince(); }; }; options.error = function() { if (active) { setTimeout(getChangesSince, timeout); timeout = timeout * 2; } }; // actually make the changes request function getChangesSince() { var opts = $.extend({heartbeat : 10 * 1000}, options, { feed : "longpoll", since : since }); ajax( {url: db.uri + "_changes"+encodeOptions(opts)}, options, "Error connecting to "+db.uri+"/_changes." ); } // start the first request if (since) { getChangesSince(); } else { db.info({ success : function(info) { since = info.update_seq; getChangesSince(); } }); } return promise; }, /** * Fetch all the docs in this db, you can specify an array of keys to * fetch by passing the keys field in the * options * parameter. * @see docs for /db/all_docs/ * @param {ajaxSettings} options jQuery ajax settings */ allDocs: function(options) { var type = "GET"; var data = null; if (options["keys"]) { type = "POST"; var keys = options["keys"]; delete options["keys"]; data = toJSON({ "keys": keys }); } ajax({ type: type, data: data, url: this.uri + "_all_docs" + encodeOptions(options) }, options, "An error occurred retrieving a list of all documents" ); }, /** * Fetch all the design docs in this db * @param {ajaxSettings} options jQuery ajax settings */ allDesignDocs: function(options) { this.allDocs($.extend( {startkey:"_design", endkey:"_design0"}, options)); }, /** * Fetch all the design docs with an index.html, options * parameter expects an eachApp field which is a callback * called on each app found. * @param {ajaxSettings} options jQuery ajax settings */ allApps: function(options) { options = options || {}; var self = this; if (options.eachApp) { this.allDesignDocs({ success: function(resp) { $.each(resp.rows, function() { self.openDoc(this.id, { success: function(ddoc) { var index, appPath, appName = ddoc._id.split('/'); appName.shift(); appName = appName.join('/'); index = ddoc.couchapp && ddoc.couchapp.index; if (index) { appPath = ['', name, ddoc._id, index].join('/'); } else if (ddoc._attachments && ddoc._attachments["index.html"]) { appPath = ['', name, ddoc._id, "index.html"].join('/'); } if (appPath) options.eachApp(appName, appPath, ddoc); } }); }); } }); } else { throw 'Please provide an eachApp function for allApps()'; } }, /** * Returns the specified doc from the specified db. * @see docs for GET /db/doc * @param {String} docId id of document to fetch * @param {ajaxSettings} options jQuery ajax settings * @param {ajaxSettings} ajaxOptions jQuery ajax settings */ openDoc: function(docId, options, ajaxOptions) { options = options || {}; if (db_opts.attachPrevRev || options.attachPrevRev) { $.extend(options, { beforeSuccess : function(req, doc) { rawDocs[doc._id] = { rev : doc._rev, raw : req.responseText }; } }); } else { $.extend(options, { beforeSuccess : function(req, doc) { if (doc["jquery.couch.attachPrevRev"]) { rawDocs[doc._id] = { rev : doc._rev, raw : req.responseText }; } } }); } ajax({url: this.uri + encodeDocId(docId) + encodeOptions(options)}, options, "The document could not be retrieved", ajaxOptions ); }, /** * Create a new document in the specified database, using the supplied * JSON document structure. If the JSON structure includes the _id * field, then the document will be created with the specified document * ID. If the _id field is not specified, a new unique ID will be * generated. * @see docs for GET /db/doc * @param {String} doc document to save * @param {ajaxSettings} options jQuery ajax settings */ saveDoc: function(doc, options) { options = options || {}; var db = this; var beforeSend = fullCommit(options); if (doc._id === undefined) { var method = "POST"; var uri = this.uri; } else { var method = "PUT"; var uri = this.uri + encodeDocId(doc._id); } var versioned = maybeApplyVersion(doc); $.ajax({ type: method, url: uri + encodeOptions(options), contentType: "application/json", dataType: "json", data: toJSON(doc), beforeSend : beforeSend, complete: function(req) { var resp = $.parseJSON(req.responseText); if (req.status == 200 || req.status == 201 || req.status == 202) { doc._id = resp.id; doc._rev = resp.rev; if (versioned) { db.openDoc(doc._id, { attachPrevRev : true, success : function(d) { doc._attachments = d._attachments; if (options.success) options.success(resp); } }); } else { if (options.success) options.success(resp); } } else if (options.error) { options.error(req.status, resp.error, resp.reason); } else { throw "The document could not be saved: " + resp.reason; } } }); }, /** * Save a list of documents * @see docs for /db/_bulk_docs * @param {Object[]} docs List of documents to save * @param {ajaxSettings} options jQuery ajax settings */ bulkSave: function(docs, options) { var beforeSend = fullCommit(options); $.extend(options, {successStatus: 201, beforeSend : beforeSend}); ajax({ type: "POST", url: this.uri + "_bulk_docs" + encodeOptions(options), contentType: "application/json", data: toJSON(docs) }, options, "The documents could not be saved" ); }, /** * Deletes the specified document from the database. You must supply * the current (latest) revision and id of the document * to delete eg removeDoc({_id:"mydoc", _rev: "1-2345"}) * @see docs for DELETE /db/doc * @param {Object} doc Document to delete * @param {ajaxSettings} options jQuery ajax settings */ removeDoc: function(doc, options) { ajax({ type: "DELETE", url: this.uri + encodeDocId(doc._id) + encodeOptions({rev: doc._rev}) }, options, "The document could not be deleted" ); }, /** * Remove a set of documents * @see docs for /db/_bulk_docs * @param {String[]} docs List of document id's to remove * @param {ajaxSettings} options jQuery ajax settings */ bulkRemove: function(docs, options){ docs.docs = $.each( docs.docs, function(i, doc){ doc._deleted = true; } ); $.extend(options, {successStatus: 201}); ajax({ type: "POST", url: this.uri + "_bulk_docs" + encodeOptions(options), data: toJSON(docs) }, options, "The documents could not be deleted" ); }, /** * The COPY command (which is non-standard HTTP) copies an existing * document to a new or existing document. * @see docs for COPY /db/doc * @param {String[]} docId document id to copy * @param {ajaxSettings} options jQuery ajax settings * @param {ajaxSettings} options jQuery ajax settings */ copyDoc: function(docId, options, ajaxOptions) { ajaxOptions = $.extend(ajaxOptions, { complete: function(req) { var resp = $.parseJSON(req.responseText); if (req.status == 201) { if (options.success) options.success(resp); } else if (options.error) { options.error(req.status, resp.error, resp.reason); } else { throw "The document could not be copied: " + resp.reason; } } }); ajax({ type: "COPY", url: this.uri + encodeDocId(docId) }, options, "The document could not be copied", ajaxOptions ); }, /** * Creates (and executes) a temporary view based on the view function * supplied in the JSON request. * @see docs for /db/_temp_view * @param {Function} mapFun Map function * @param {Function} reduceFun Reduce function * @param {Function} language Language the map / reduce funs are * implemented in * @param {ajaxSettings} options jQuery ajax settings */ query: function(mapFun, reduceFun, language, options) { language = language || "javascript"; if (typeof(mapFun) !== "string") { mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")"; } var body = {language: language, map: mapFun}; if (reduceFun != null) { if (typeof(reduceFun) !== "string") reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")"; body.reduce = reduceFun; } ajax({ type: "POST", url: this.uri + "_temp_view" + encodeOptions(options), contentType: "application/json", data: toJSON(body) }, options, "An error occurred querying the database" ); }, /** * Fetch a _list view output, you can specify a list of * keys in the options object to recieve only those keys. * @see * docs for /db/_design/design-doc/_list/l1/v1 * @param {String} list Listname in the form of ddoc/listname * @param {String} view View to run list against * @param {options} CouchDB View Options * @param {ajaxSettings} options jQuery ajax settings */ list: function(list, view, options, ajaxOptions) { var list = list.split('/'); var options = options || {}; var type = 'GET'; var data = null; if (options['keys']) { type = 'POST'; var keys = options['keys']; delete options['keys']; data = toJSON({'keys': keys }); } ajax({ type: type, data: data, url: this.uri + '_design/' + list[0] + '/_list/' + list[1] + '/' + view + encodeOptions(options) }, ajaxOptions, 'An error occured accessing the list' ); }, /** * Executes the specified view-name from the specified design-doc * design document, you can specify a list of keys * in the options object to recieve only those keys. * @see docs for /db/ * _design/design-doc/_list/l1/v1 * @param {String} name View to run list against (string should have * the design-doc name followed by a slash and the view name) * @param {ajaxSettings} options jQuery ajax settings */ view: function(name, options) { var name = name.split('/'); var options = options || {}; var type = "GET"; var data= null; if (options["keys"]) { type = "POST"; var keys = options["keys"]; delete options["keys"]; data = toJSON({ "keys": keys }); } ajax({ type: type, data: data, url: this.uri + "_design/" + name[0] + "/_view/" + name[1] + encodeOptions(options) }, options, "An error occurred accessing the view" ); }, /** * Fetch an arbitrary CouchDB database property * @see docs for /db/_prop * @param {String} propName Propery name to fetch * @param {ajaxSettings} options jQuery ajax settings * @param {ajaxSettings} ajaxOptions jQuery ajax settings */ getDbProperty: function(propName, options, ajaxOptions) { ajax({url: this.uri + propName + encodeOptions(options)}, options, "The property could not be retrieved", ajaxOptions ); }, /** * Set an arbitrary CouchDB database property * @see docs for /db/_prop * @param {String} propName Propery name to fetch * @param {String} propValue Propery value to set * @param {ajaxSettings} options jQuery ajax settings * @param {ajaxSettings} ajaxOptions jQuery ajax settings */ setDbProperty: function(propName, propValue, options, ajaxOptions) { ajax({ type: "PUT", url: this.uri + propName + encodeOptions(options), data : JSON.stringify(propValue) }, options, "The property could not be updated", ajaxOptions ); } }; }, encodeDocId: encodeDocId, /** * Accessing the root of a CouchDB instance returns meta information about * the instance. The response is a JSON structure containing information * about the server, including a welcome message and the version of the * server. * @see * docs for GET / * @param {ajaxSettings} options jQuery ajax settings */ info: function(options) { ajax( {url: this.urlPrefix + "/"}, options, "Server information could not be retrieved" ); }, /** * Request, configure, or stop, a replication operation. * @see docs for POST /_replicate * @param {String} source Path or url to source database * @param {String} target Path or url to target database * @param {ajaxSettings} ajaxOptions jQuery ajax settings * @param {Object} repOpts Additional replication options */ replicate: function(source, target, ajaxOptions, repOpts) { repOpts = $.extend({source: source, target: target}, repOpts); if (repOpts.continuous && !repOpts.cancel) { ajaxOptions.successStatus = 202; } ajax({ type: "POST", url: this.urlPrefix + "/_replicate", data: JSON.stringify(repOpts), contentType: "application/json" }, ajaxOptions, "Replication failed" ); }, /** * Fetch a new UUID * @see docs for /_uuids * @param {Int} cacheNum Number of uuids to keep cached for future use */ newUUID: function(cacheNum) { if (cacheNum === undefined) { cacheNum = 1; } if (!uuidCache.length) { ajax({url: this.urlPrefix + "/_uuids", data: {count: cacheNum}, async: false}, { success: function(resp) { uuidCache = resp.uuids; } }, "Failed to retrieve UUID batch." ); } return uuidCache.shift(); } }); /** * @private */ function ajax(obj, options, errorMessage, ajaxOptions) { var timeStart; var defaultAjaxOpts = { contentType: "application/json", headers:{"Accept": "application/json"} }; options = $.extend({successStatus: 200}, options); ajaxOptions = $.extend(defaultAjaxOpts, ajaxOptions); errorMessage = errorMessage || "Unknown error"; timeStart = (new Date()).getTime(); $.ajax($.extend($.extend({ type: "GET", dataType: "json", cache : !$.browser.msie, beforeSend: function(xhr){ if(ajaxOptions && ajaxOptions.headers){ for (var header in ajaxOptions.headers){ xhr.setRequestHeader(header, ajaxOptions.headers[header]); } } }, complete: function(req) { var reqDuration = (new Date()).getTime() - timeStart; try { var resp = $.parseJSON(req.responseText); } catch(e) { if (options.error) { options.error(req.status, req, e); } else { throw errorMessage + ': ' + e; } return; } if (options.ajaxStart) { options.ajaxStart(resp); } if (req.status == options.successStatus) { if (options.beforeSuccess) options.beforeSuccess(req, resp, reqDuration); if (options.success) options.success(resp, reqDuration); } else if (options.error) { options.error(req.status, resp && resp.error || errorMessage, resp && resp.reason || "no response", reqDuration); } else { throw errorMessage + ": " + resp.reason; } } }, obj), ajaxOptions)); } /** * @private */ function fullCommit(options) { var options = options || {}; if (typeof options.ensure_full_commit !== "undefined") { var commit = options.ensure_full_commit; delete options.ensure_full_commit; return function(xhr) { xhr.setRequestHeader('Accept', 'application/json'); xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString()); }; } }; /** * @private */ // Convert a options object to an url query string. // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"' function encodeOptions(options) { var buf = []; if (typeof(options) === "object" && options !== null) { for (var name in options) { if ($.inArray(name, ["error", "success", "beforeSuccess", "ajaxStart"]) >= 0) continue; var value = options[name]; if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) { value = toJSON(value); } buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); } } return buf.length ? "?" + buf.join("&") : ""; } /** * @private */ function toJSON(obj) { return obj !== null ? JSON.stringify(obj) : null; } })(jQuery); ================================================ FILE: assets/js/lib/jquery.mb.browser.js ================================================ /* * ****************************************************************************** * jquery.mb.components * file: jquery.mb.browser.min.js * * Copyright (c) 2001-2014. Matteo Bicocchi (Pupunzi); * Open lab srl, Firenze - Italy * email: matteo@open-lab.com * site: http://pupunzi.com * blog: http://pupunzi.open-lab.com * http://open-lab.com * * Licences: MIT, GPL * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * last modified: 26/03/14 21.43 * ***************************************************************************** */ var nAgt=navigator.userAgent;if(!jQuery.browser){jQuery.browser={},jQuery.browser.mozilla=!1,jQuery.browser.webkit=!1,jQuery.browser.opera=!1,jQuery.browser.safari=!1,jQuery.browser.chrome=!1,jQuery.browser.msie=!1,jQuery.browser.ua=nAgt,jQuery.browser.name=navigator.appName,jQuery.browser.fullVersion=""+parseFloat(navigator.appVersion),jQuery.browser.majorVersion=parseInt(navigator.appVersion,10);var nameOffset,verOffset,ix;if(-1!=(verOffset=nAgt.indexOf("Opera")))jQuery.browser.opera=!0,jQuery.browser.name="Opera",jQuery.browser.fullVersion=nAgt.substring(verOffset+6),-1!=(verOffset=nAgt.indexOf("Version"))&&(jQuery.browser.fullVersion=nAgt.substring(verOffset+8));else if(-1!=(verOffset=nAgt.indexOf("OPR")))jQuery.browser.opera=!0,jQuery.browser.name="Opera",jQuery.browser.fullVersion=nAgt.substring(verOffset+4);else if(-1!=(verOffset=nAgt.indexOf("MSIE")))jQuery.browser.msie=!0,jQuery.browser.name="Microsoft Internet Explorer",jQuery.browser.fullVersion=nAgt.substring(verOffset+5);else if(-1!=nAgt.indexOf("Trident")){jQuery.browser.msie=!0,jQuery.browser.name="Microsoft Internet Explorer";var start=nAgt.indexOf("rv:")+3,end=start+4;jQuery.browser.fullVersion=nAgt.substring(start,end)}else-1!=(verOffset=nAgt.indexOf("Chrome"))?(jQuery.browser.webkit=!0,jQuery.browser.chrome=!0,jQuery.browser.name="Chrome",jQuery.browser.fullVersion=nAgt.substring(verOffset+7)):-1!=(verOffset=nAgt.indexOf("Safari"))?(jQuery.browser.webkit=!0,jQuery.browser.safari=!0,jQuery.browser.name="Safari",jQuery.browser.fullVersion=nAgt.substring(verOffset+7),-1!=(verOffset=nAgt.indexOf("Version"))&&(jQuery.browser.fullVersion=nAgt.substring(verOffset+8))):-1!=(verOffset=nAgt.indexOf("AppleWebkit"))?(jQuery.browser.webkit=!0,jQuery.browser.name="Safari",jQuery.browser.fullVersion=nAgt.substring(verOffset+7),-1!=(verOffset=nAgt.indexOf("Version"))&&(jQuery.browser.fullVersion=nAgt.substring(verOffset+8))):-1!=(verOffset=nAgt.indexOf("Firefox"))?(jQuery.browser.mozilla=!0,jQuery.browser.name="Firefox",jQuery.browser.fullVersion=nAgt.substring(verOffset+8)):(nameOffset=nAgt.lastIndexOf(" ")+1)<(verOffset=nAgt.lastIndexOf("/"))&&(jQuery.browser.name=nAgt.substring(nameOffset,verOffset),jQuery.browser.fullVersion=nAgt.substring(verOffset+1),jQuery.browser.name.toLowerCase()==jQuery.browser.name.toUpperCase()&&(jQuery.browser.name=navigator.appName));-1!=(ix=jQuery.browser.fullVersion.indexOf(";"))&&(jQuery.browser.fullVersion=jQuery.browser.fullVersion.substring(0,ix)),-1!=(ix=jQuery.browser.fullVersion.indexOf(" "))&&(jQuery.browser.fullVersion=jQuery.browser.fullVersion.substring(0,ix)),jQuery.browser.majorVersion=parseInt(""+jQuery.browser.fullVersion,10),isNaN(jQuery.browser.majorVersion)&&(jQuery.browser.fullVersion=""+parseFloat(navigator.appVersion),jQuery.browser.majorVersion=parseInt(navigator.appVersion,10)),jQuery.browser.version=jQuery.browser.majorVersion}jQuery.browser.android=/Android/i.test(nAgt),jQuery.browser.blackberry=/BlackBerry|BB|PlayBook/i.test(nAgt),jQuery.browser.ios=/iPhone|iPad|iPod|webOS/i.test(nAgt),jQuery.browser.operaMobile=/Opera Mini/i.test(nAgt),jQuery.browser.windowsMobile=/IEMobile|Windows Phone/i.test(nAgt),jQuery.browser.kindle=/Kindle|Silk/i.test(nAgt),jQuery.browser.mobile=jQuery.browser.android||jQuery.browser.blackberry||jQuery.browser.ios||jQuery.browser.windowsMobile||jQuery.browser.operaMobile||jQuery.browser.kindle,jQuery.isMobile=jQuery.browser.mobile,jQuery.isTablet=jQuery.browser.mobile&&jQuery(window).width()>765,jQuery.isAndroidDefault=jQuery.browser.android&&!/chrome/i.test(nAgt); ================================================ FILE: assets/js/lib/lodash.js ================================================ /** * @license * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE * Build: `lodash modern -o ./dist/lodash.js` */ ;(function(){function n(n,t,e){e=(e||0)-1;for(var r=n?n.length:0;++ea||typeof i=="undefined")return 1;if(ie?0:e);++r=b&&i===n,l=[];if(f){var p=o(r);p?(i=t,r=p):f=false}for(;++ui(r,p)&&l.push(p);return f&&c(r),l}function ut(n,t,e,r){r=(r||0)-1;for(var u=n?n.length:0,o=[];++r=b&&f===n,h=u||v?a():s; for(v&&(h=o(h),f=t);++if(h,y))&&((u||v)&&h.push(y),s.push(g))}return v?(l(h.k),c(h)):u&&l(h),s}function lt(n){return function(t,e,r){var u={};e=J.createCallback(e,r,3),r=-1;var o=t?t.length:0;if(typeof o=="number")for(;++re?Ie(0,o+e):e)||0,Te(n)?i=-1o&&(o=a)}}else t=null==t&&kt(n)?r:J.createCallback(t,e,3),St(n,function(n,e,r){e=t(n,e,r),e>u&&(u=e,o=n)});return o}function Dt(n,t,e,r){if(!n)return e;var u=3>arguments.length;t=J.createCallback(t,r,4);var o=-1,i=n.length;if(typeof i=="number")for(u&&(e=n[++o]);++oarguments.length;return t=J.createCallback(t,r,4),Et(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)}),e}function Tt(n){var t=-1,e=n?n.length:0,r=Xt(typeof e=="number"?e:0);return St(n,function(n){var e=at(0,++t);r[t]=r[e],r[e]=n}),r}function Ft(n,t,e){var r;t=J.createCallback(t,e,3),e=-1;var u=n?n.length:0;if(typeof u=="number")for(;++er?Ie(0,u+r):r||0}else if(r)return r=zt(t,e),t[r]===e?r:-1;return n(t,e,r)}function qt(n,t,e){if(typeof t!="number"&&null!=t){var r=0,u=-1,o=n?n.length:0;for(t=J.createCallback(t,e,3);++u>>1,e(n[r])e?0:e);++t=v; m?(i&&(i=ve(i)),s=f,a=n.apply(l,o)):i||(i=_e(r,v))}return m&&c?c=ve(c):c||t===h||(c=_e(u,t)),e&&(m=true,a=n.apply(l,o)),!m||c||i||(o=l=null),a}}function Ut(n){return n}function Gt(n,t,e){var r=true,u=t&&bt(t);t&&(e||u.length)||(null==e&&(e=t),o=Q,t=n,n=J,u=bt(t)),false===e?r=false:wt(e)&&"chain"in e&&(r=e.chain);var o=n,i=dt(o);St(u,function(e){var u=n[e]=t[e];i&&(o.prototype[e]=function(){var t=this.__chain__,e=this.__wrapped__,i=[e];if(be.apply(i,arguments),i=u.apply(n,i),r||t){if(e===i&&wt(i))return this; i=new o(i),i.__chain__=t}return i})})}function Ht(){}function Jt(n){return function(t){return t[n]}}function Qt(){return this.__wrapped__}e=e?Y.defaults(G.Object(),e,Y.pick(G,A)):G;var Xt=e.Array,Yt=e.Boolean,Zt=e.Date,ne=e.Function,te=e.Math,ee=e.Number,re=e.Object,ue=e.RegExp,oe=e.String,ie=e.TypeError,ae=[],fe=re.prototype,le=e._,ce=fe.toString,pe=ue("^"+oe(ce).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),se=te.ceil,ve=e.clearTimeout,he=te.floor,ge=ne.prototype.toString,ye=vt(ye=re.getPrototypeOf)&&ye,me=fe.hasOwnProperty,be=ae.push,_e=e.setTimeout,de=ae.splice,we=ae.unshift,je=function(){try{var n={},t=vt(t=re.defineProperty)&&t,e=t(n,n,n)&&t }catch(r){}return e}(),ke=vt(ke=re.create)&&ke,xe=vt(xe=Xt.isArray)&&xe,Ce=e.isFinite,Oe=e.isNaN,Ne=vt(Ne=re.keys)&&Ne,Ie=te.max,Se=te.min,Ee=e.parseInt,Re=te.random,Ae={};Ae[$]=Xt,Ae[T]=Yt,Ae[F]=Zt,Ae[B]=ne,Ae[q]=re,Ae[W]=ee,Ae[z]=ue,Ae[P]=oe,Q.prototype=J.prototype;var De=J.support={};De.funcDecomp=!vt(e.a)&&E.test(s),De.funcNames=typeof ne.name=="string",J.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:N,variable:"",imports:{_:J}},ke||(nt=function(){function n(){}return function(t){if(wt(t)){n.prototype=t; var r=new n;n.prototype=null}return r||e.Object()}}());var $e=je?function(n,t){M.value=t,je(n,"__bindData__",M)}:Ht,Te=xe||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ce.call(n)==$||false},Fe=Ne?function(n){return wt(n)?Ne(n):[]}:H,Be={"&":"&","<":"<",">":">",'"':""","'":"'"},We=_t(Be),qe=ue("("+Fe(We).join("|")+")","g"),ze=ue("["+Fe(Be).join("")+"]","g"),Pe=ye?function(n){if(!n||ce.call(n)!=q)return false;var t=n.valueOf,e=vt(t)&&(e=ye(t))&&ye(e);return e?n==e||ye(n)==e:ht(n) }:ht,Ke=lt(function(n,t,e){me.call(n,e)?n[e]++:n[e]=1}),Le=lt(function(n,t,e){(me.call(n,e)?n[e]:n[e]=[]).push(t)}),Me=lt(function(n,t,e){n[e]=t}),Ve=Rt,Ue=vt(Ue=Zt.now)&&Ue||function(){return(new Zt).getTime()},Ge=8==Ee(d+"08")?Ee:function(n,t){return Ee(kt(n)?n.replace(I,""):n,t||0)};return J.after=function(n,t){if(!dt(t))throw new ie;return function(){return 1>--n?t.apply(this,arguments):void 0}},J.assign=U,J.at=function(n){for(var t=arguments,e=-1,r=ut(t,true,false,1),t=t[2]&&t[2][t[1]]===n?1:r.length,u=Xt(t);++e=b&&o(r?e[r]:s)))}var p=e[0],h=-1,g=p?p.length:0,y=[];n:for(;++h(m?t(m,v):f(s,v))){for(r=u,(m||s).push(v);--r;)if(m=i[r],0>(m?t(m,v):f(e[r],v)))continue n;y.push(v)}}for(;u--;)(m=i[u])&&c(m);return l(i),l(s),y},J.invert=_t,J.invoke=function(n,t){var e=p(arguments,2),r=-1,u=typeof t=="function",o=n?n.length:0,i=Xt(typeof o=="number"?o:0);return St(n,function(n){i[++r]=(u?t:n[t]).apply(n,e)}),i},J.keys=Fe,J.map=Rt,J.mapValues=function(n,t,e){var r={}; return t=J.createCallback(t,e,3),h(n,function(n,e,u){r[e]=t(n,e,u)}),r},J.max=At,J.memoize=function(n,t){function e(){var r=e.cache,u=t?t.apply(this,arguments):m+arguments[0];return me.call(r,u)?r[u]:r[u]=n.apply(this,arguments)}if(!dt(n))throw new ie;return e.cache={},e},J.merge=function(n){var t=arguments,e=2;if(!wt(n))return n;if("number"!=typeof t[2]&&(e=t.length),3e?Ie(0,r+e):Se(e,r-1))+1);r--;)if(n[r]===t)return r;return-1},J.mixin=Gt,J.noConflict=function(){return e._=le,this},J.noop=Ht,J.now=Ue,J.parseInt=Ge,J.random=function(n,t,e){var r=null==n,u=null==t;return null==e&&(typeof n=="boolean"&&u?(e=n,n=1):u||typeof t!="boolean"||(e=t,u=true)),r&&u&&(t=1),n=+n||0,u?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=Re(),Se(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):at(n,t) },J.reduce=Dt,J.reduceRight=$t,J.result=function(n,t){if(n){var e=n[t];return dt(e)?n[t]():e}},J.runInContext=s,J.size=function(n){var t=n?n.length:0;return typeof t=="number"?t:Fe(n).length},J.some=Ft,J.sortedIndex=zt,J.template=function(n,t,e){var r=J.templateSettings;n=oe(n||""),e=_({},e,r);var u,o=_({},e.imports,r.imports),r=Fe(o),o=xt(o),a=0,f=e.interpolate||S,l="__p+='",f=ue((e.escape||S).source+"|"+f.source+"|"+(f===N?x:S).source+"|"+(e.evaluate||S).source+"|$","g");n.replace(f,function(t,e,r,o,f,c){return r||(r=o),l+=n.slice(a,c).replace(R,i),e&&(l+="'+__e("+e+")+'"),f&&(u=true,l+="';"+f+";\n__p+='"),r&&(l+="'+((__t=("+r+"))==null?'':__t)+'"),a=c+t.length,t }),l+="';",f=e=e.variable,f||(e="obj",l="with("+e+"){"+l+"}"),l=(u?l.replace(w,""):l).replace(j,"$1").replace(k,"$1;"),l="function("+e+"){"+(f?"":e+"||("+e+"={});")+"var __t,__p='',__e=_.escape"+(u?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}";try{var c=ne(r,"return "+l).apply(v,o)}catch(p){throw p.source=l,p}return t?c(t):(c.source=l,c)},J.unescape=function(n){return null==n?"":oe(n).replace(qe,gt)},J.uniqueId=function(n){var t=++y;return oe(null==n?"":n)+t },J.all=Ot,J.any=Ft,J.detect=It,J.findWhere=It,J.foldl=Dt,J.foldr=$t,J.include=Ct,J.inject=Dt,Gt(function(){var n={};return h(J,function(t,e){J.prototype[e]||(n[e]=t)}),n}(),false),J.first=Bt,J.last=function(n,t,e){var r=0,u=n?n.length:0;if(typeof t!="number"&&null!=t){var o=u;for(t=J.createCallback(t,e,3);o--&&t(n[o],o,n);)r++}else if(r=t,null==r||e)return n?n[u-1]:v;return p(n,Ie(0,u-r))},J.sample=function(n,t,e){return n&&typeof n.length!="number"&&(n=xt(n)),null==t||e?n?n[at(0,n.length-1)]:v:(n=Tt(n),n.length=Se(Ie(0,t),n.length),n) },J.take=Bt,J.head=Bt,h(J,function(n,t){var e="sample"!==t;J.prototype[t]||(J.prototype[t]=function(t,r){var u=this.__chain__,o=n(this.__wrapped__,t,r);return u||null!=t&&(!r||e&&typeof t=="function")?new Q(o,u):o})}),J.VERSION="2.4.1",J.prototype.chain=function(){return this.__chain__=true,this},J.prototype.toString=function(){return oe(this.__wrapped__)},J.prototype.value=Qt,J.prototype.valueOf=Qt,St(["join","pop","shift"],function(n){var t=ae[n];J.prototype[n]=function(){var n=this.__chain__,e=t.apply(this.__wrapped__,arguments); return n?new Q(e,n):e}}),St(["push","reverse","sort","unshift"],function(n){var t=ae[n];J.prototype[n]=function(){return t.apply(this.__wrapped__,arguments),this}}),St(["concat","slice","splice"],function(n){var t=ae[n];J.prototype[n]=function(){return new Q(t.apply(this.__wrapped__,arguments),this.__chain__)}}),J}var v,h=[],g=[],y=0,m=+new Date+"",b=75,_=40,d=" \t\x0B\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",w=/\b__p\+='';/g,j=/\b(__p\+=)''\+/g,k=/(__e\(.*?\)|\b__t\))\+'';/g,x=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,C=/\w*$/,O=/^\s*function[ \n\r\t]+\w/,N=/<%=([\s\S]+?)%>/g,I=RegExp("^["+d+"]*0+(?=.$)"),S=/($^)/,E=/\bthis\b/,R=/['\n\r\t\u2028\u2029\\]/g,A="Array Boolean Date Function Math Number Object RegExp String _ attachEvent clearTimeout isFinite isNaN parseInt setTimeout".split(" "),D="[object Arguments]",$="[object Array]",T="[object Boolean]",F="[object Date]",B="[object Function]",W="[object Number]",q="[object Object]",z="[object RegExp]",P="[object String]",K={}; K[B]=false,K[D]=K[$]=K[T]=K[F]=K[W]=K[q]=K[z]=K[P]=true;var L={leading:false,maxWait:0,trailing:false},M={configurable:false,enumerable:false,value:null,writable:false},V={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},U={"\\":"\\","'":"'","\n":"n","\r":"r","\t":"t","\u2028":"u2028","\u2029":"u2029"},G=V[typeof window]&&window||this,H=V[typeof exports]&&exports&&!exports.nodeType&&exports,J=V[typeof module]&&module&&!module.nodeType&&module,Q=J&&J.exports===H&&H,X=V[typeof global]&&global;!X||X.global!==X&&X.window!==X||(G=X); var Y=s();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(G._=Y, define(function(){return Y})):H&&J?Q?(J.exports=Y)._=Y:H._=Y:G._=Y}).call(this); ================================================ FILE: assets/js/lib/modalbox.js ================================================ "use strict"; define([ 'jquery', 'sourceModules/module', 'sourceLib/lodash' ], function($, module, _) { var context; // instance of box var ModalBox = function(config, data) { var isNewInstance = !context; context = context || this; context.data = data ? data : initialBlocksData; if (!isNewInstance) { context.render(); return context; } var modulesOptions = context.options.modulesOptions; modulesOptions.modalBox = $.extend(true, { "classes": { "box": [context.options.mainClass, context.options.colMain, "source_modal_box"].join(' '), "title": "source_modal_title", "body": "source_modal_body", "close": "source_modal_close" }, "labels": { "close": "Close extended search results" }, "appendTo": ".source_main" }, modulesOptions.modalBox, config); context.init(); }; ModalBox.prototype = module.createInstance(); ModalBox.prototype.constructor = ModalBox; ModalBox.prototype.init = function() { this.render(); this.initCloseHandler(); }; ModalBox.prototype.templates = { box: _.template('
'), title: _.template('
'), close: _.template('
'), body: _.template('
') }; ModalBox.prototype.renderers = {}; ModalBox.prototype.renderers.box = function(element) { $(this.options.modulesOptions.modalBox.appendTo).after(element); }; ModalBox.prototype.renderers.close = function(element) { $(element).html(this.data.close ? this.data.close : ""); $(this.box).append(element); }; ModalBox.prototype.renderers.title = function(element) { $(element).html(this.data.title ? this.data.title : ""); $(this.box).append(element); }; ModalBox.prototype.renderers.body = function(element) { $(element).html(this.data.body ? this.data.body : ""); $(this.box).append(element); }; ModalBox.prototype.render = function() { var config = this.options.modulesOptions.modalBox; $.each(context.renderers, function(name, callback) { var element = context[name] = context[name] ? context[name] : $(context.templates[name].call(context, config)); callback.call(context, element); }); }; ModalBox.prototype.initCloseHandler = function() { $(this.close).on("click", function(e) { context.hide(); }); }; ModalBox.prototype.show = function() { var config = this.options.modulesOptions.modalBox; $(config.appendTo).hide(); $(this.box).show(); return this; }; ModalBox.prototype.hide = function() { var config = this.options.modulesOptions.modalBox; $(config.appendTo).show(); $(this.box).hide(); return this; }; return ModalBox; }); ================================================ FILE: assets/js/lib/prism/prism.css ================================================ /** * prism.js default theme for JavaScript, CSS and HTML * Based on dabblet (http://dabblet.com) * @author Lea Verou */ pre[class*="src-"], pre[class*="src-"] code { color: #000; font: 12px/1.5 Consolas, Menlo, Monaco, 'Andale Mono', monospace; border-radius:10px; border:0; direction: ltr; text-align: left; white-space: pre; word-spacing: normal; position: relative; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; -webkit-hyphens: none; -moz-hyphens: none; -ms-hyphens: none; hyphens: none; } pre[class*="src-"] code.__visible { opacity:1; -webkit-transition: opacity .3s; -moz-transition: opacity .3s; -o-transition: opacity .3s; transition: opacity .3s; } @media print { pre[class*="src-"], pre[class*="src-"] code { text-shadow: none; } } /* Code blocks */ pre[class*="src-"] { counter-reset: linenumber; padding: 10px 20px; margin: .5em 0; overflow: auto; resize:horizontal; } :not(pre) > code[class*="src-"], pre[class*="src-"] { background: #f1f2f3; border:1px solid #dfe2e4; -moz-box-sizing: border-box; box-sizing: border-box; } /* Inline code */ :not(pre) > code[class*="src-"] { padding: .1em; border-radius: .3em; } .token.comment, .token.prolog, .token.doctype, .token.cdata { color: slategray; } .token.punctuation { color: #999; } .namespace { opacity: .7; } .token.property, .token.prism_tag, .token.boolean, .token.number, .token.constant, .token.symbol { color: #905; } .token.selector, .token.attr-name, .token.string, .token.builtin { color: #690; } .token.operator, .token.entity, .token.url, .src-css .token.string, .style .token.string, .token.variable { color: #a67f59; } .token.atrule, .token.attr-value, .token.keyword { color: #07a; } .token.regex, .token.important { color: #e90; } .token.important { font-weight: bold; } .token.entity { cursor: help; } .token.json-property { color: #07A; } .token.old { margin-right: .25em; color: #f66; text-decoration:line-through } .token.new { color:#690 } .line-numbers-rows { position: absolute; pointer-events: none; top: 0; font-size: 100%; left: -3.8em; width: 3em; /* works for line-numbers below 1000 lines */ letter-spacing: -1px; border-right: 1px solid #aaa; line-height:1.5; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .line-numbers-rows > span { pointer-events: none; display: block; counter-increment: linenumber; } .line-numbers-rows > span:before { content: counter(linenumber); color: #aaa; display: block; padding-right: 0.8em; text-align: right; } ================================================ FILE: assets/js/lib/prism/prism.js ================================================ /** * Prism: Lightweight, robust, elegant syntax highlighting * MIT license http://www.opensource.org/licenses/mit-license.php/ * @author Lea Verou http://lea.verou.me */ (function(){ // Private helper vars var lang = /\bsrc?-(?!\*)(\w+)\b/i; var _ = self.Prism = { util: { type: function (o) { return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1]; }, // Deep clone a language definition (e.g. to extend it) clone: function (o) { var type = _.util.type(o); switch (type) { case 'Object': var clone = {}; for (var key in o) { if (o.hasOwnProperty(key)) { clone[key] = _.util.clone(o[key]); } } return clone; case 'Array': return o.slice(); } return o; } }, languages: { extend: function (id, redef) { var lang = _.util.clone(_.languages[id]); for (var key in redef) { lang[key] = redef[key]; } return lang; }, // Insert a token before another token in a language literal insertBefore: function (inside, before, insert, root) { root = root || _.languages; var grammar = root[inside]; var ret = {}; for (var token in grammar) { if (grammar.hasOwnProperty(token)) { if (token == before) { for (var newToken in insert) { if (insert.hasOwnProperty(newToken)) { ret[newToken] = insert[newToken]; } } } ret[token] = grammar[token]; } } return root[inside] = ret; }, // Traverse a language definition with Depth First Search DFS: function(o, callback) { for (var i in o) { callback.call(o, i, o[i]); if (_.util.type(o) === 'Object') { _.languages.DFS(o[i], callback); } } } }, highlightAll: function(async, callback) { // var elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'); var elements = document.querySelectorAll('pre[class*="src"] code'); for (var i=0, element; element = elements[i++];) { _.highlightElement(element, async === true, callback); } }, highlightElement: function(element, async, callback) { // Find language var language, grammar, parent = element; while (parent && !lang.test(parent.className)) { parent = parent.parentNode; } if (parent) { language = (parent.className.match(lang) || [,''])[1]; grammar = _.languages[language]; } if (!grammar) { return; } // Set language on the element, if not present // element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + 'lang-' + language; // Set language on the parent, for styling parent = element.parentNode; if (/pre/i.test(parent.nodeName)) { parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' src-' + language; } var code = element.textContent; if(!code) { return; } code = code.replace(/&/g, '&').replace(/ text.length) { // Something went terribly wrong, ABORT, ABORT! break tokenloop; } if (str instanceof Token) { continue; } pattern.lastIndex = 0; var match = pattern.exec(str); if (match) { if(lookbehind) { lookbehindLength = match[1].length; } var from = match.index - 1 + lookbehindLength, match = match[0].slice(lookbehindLength), len = match.length, to = from + len, before = str.slice(0, from + 1), after = str.slice(to + 1); var args = [i, 1]; if (before) { args.push(before); } var wrapped = new Token(token, inside? _.tokenize(match, inside) : match); args.push(wrapped); if (after) { args.push(after); } Array.prototype.splice.apply(strarr, args); } } } return strarr; }, hooks: { all: {}, add: function (name, callback) { var hooks = _.hooks.all; hooks[name] = hooks[name] || []; hooks[name].push(callback); }, run: function (name, env) { var callbacks = _.hooks.all[name]; if (!callbacks || !callbacks.length) { return; } for (var i=0, callback; callback = callbacks[i++];) { callback(env); } } } }; var Token = _.Token = function(type, content) { this.type = type; this.content = content; }; Token.stringify = function(o, language, parent) { if (typeof o == 'string') { return o; } if (Object.prototype.toString.call(o) == '[object Array]') { return o.map(function(element) { return Token.stringify(element, language, o); }).join(''); } var env = { type: o.type, content: Token.stringify(o.content, language, parent), prism_tag: 'span', classes: ['token', o.type], attributes: {}, language: language, parent: parent }; if (env.type == 'comment') { env.attributes['spellcheck'] = 'true'; } _.hooks.run('wrap', env); var attributes = ''; for (var name in env.attributes) { attributes += name + '="' + (env.attributes[name] || '') + '"'; } return '<' + env.prism_tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + ''; }; if (!self.document) { // In worker self.addEventListener('message', function(evt) { var message = JSON.parse(evt.data), lang = message.language, code = message.code; self.postMessage(JSON.stringify(_.tokenize(code, _.languages[lang]))); self.close(); }, false); return; } // Get current script and highlight var script = document.getElementsByTagName('script'); script = script[script.length - 1]; if (script) { _.filename = script.src; if (document.addEventListener && !script.hasAttribute('data-manual')) { document.addEventListener('DOMContentLoaded', _.highlightAll); } } })(); Prism.languages.html = { 'comment': /<!--[\w\W]*?-->/g, 'prolog': /<\?.+?\?>/, 'doctype': /<!DOCTYPE.+?>/, 'cdata': /<!\[CDATA\[[\w\W]*?]]>/i, 'prism_tag': { pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi, inside: { 'prism_tag': { pattern: /^<\/?[\w:-]+/i, inside: { 'punctuation': /^<\/?/, 'namespace': /^[\w-]+?:/ } }, 'attr-value': { pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi, inside: { 'punctuation': /=|>|"/g } }, 'punctuation': /\/?>/g, 'attr-name': { pattern: /[\w:-]+/g, inside: { 'namespace': /^[\w-]+?:/ } } } }, 'entity': /&#?[\da-z]{1,8};/gi }; // Plugin to make entity title show the real entity, idea by Roman Komarov Prism.hooks.add('wrap', function(env) { if (env.type === 'entity') { env.attributes['title'] = env.content.replace(/&/, '&'); } });; Prism.languages.css = { 'comment': /\/\*[\w\W]*?\*\//g, 'atrule': { pattern: /@[\w-]+?.*?(;|(?=\s*{))/gi, inside: { 'punctuation': /[;:]/g } }, 'url': /url\((["']?).*?\1\)/gi, 'selector': /[^\{\}\s][^\{\};]*(?=\s*\{)/g, 'property': /(\b|\B)[\w-]+(?=\s*:)/ig, 'string': /("|')(\\?.)*?\1/g, 'important': /\B!important\b/gi, 'ignore': /&(lt|gt|amp);/gi, 'punctuation': /[\{\};:]/g }; if (Prism.languages.html) { Prism.languages.insertBefore('html', 'prism_tag', { 'style': { pattern: /(<|<)style[\w\W]*?(>|>)[\w\W]*?(<|<)\/style(>|>)/ig, inside: { 'prism_tag': { pattern: /(<|<)style[\w\W]*?(>|>)|(<|<)\/style(>|>)/ig, inside: Prism.languages.html.prism_tag.inside }, rest: Prism.languages.css } } }); }; Prism.languages.clike = { 'comment': { pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g, lookbehind: true }, 'string': /("|')(\\?.)*?\1/g, 'class-name': { pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig, lookbehind: true, inside: { punctuation: /(\.|\\)/ } }, 'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g, 'boolean': /\b(true|false)\b/g, 'function': { pattern: /[a-z0-9_]+\(/ig, inside: { punctuation: /\(/ } }, 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g, 'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|(&){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g, 'ignore': /&(lt|gt|amp);/gi, 'punctuation': /[{}[\];(),.:]/g }; ; Prism.languages.js = Prism.languages.extend('clike', { 'keyword': /\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g, 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g }); Prism.languages.insertBefore('js', 'keyword', { 'regex': { pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g, lookbehind: true } }); if (Prism.languages.html) { Prism.languages.insertBefore('html', 'prism_tag', { 'script': { pattern: /(<|<)script[\w\W]*?(>|>)[\w\W]*?(<|<)\/script(>|>)/ig, inside: { 'prism_tag': { pattern: /(<|<)script[\w\W]*?(>|>)|(<|<)\/script(>|>)/ig, inside: Prism.languages.html.prism_tag.inside }, rest: Prism.languages.js } } }); } ; Prism.languages.bash = Prism.languages.extend('clike', { 'comment': { pattern: /(^|[^"{\\])(#.*?(\r?\n|$))/g, lookbehind: true }, 'string': { //allow multiline string pattern: /("|')(\\?[\s\S])*?\1/g, inside: { //'property' class reused for bash variables 'property': /\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/g } }, 'keyword': /\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/g }); Prism.languages.insertBefore('bash', 'keyword', { //'property' class reused for bash variables 'property': /\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/g }); Prism.languages.insertBefore('bash', 'comment', { //shebang must be before comment, 'important' class from css reused 'important': /(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/g }); ================================================ FILE: assets/js/lib/require.js ================================================ /** vim: et:ts=4:sw=4:sts=4 * @license RequireJS 2.1.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ //Not using strict: uneven strict support in browsers, #392, and causes //problems with requirejs.exec()/transpiler plugins that may not be strict. /*jslint regexp: true, nomen: true, sloppy: true */ /*global window, navigator, document, importScripts, setTimeout, opera */ var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, version = '2.1.14', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, currDirRegExp = /^\.\//, op = Object.prototype, ostring = op.toString, hasOwn = op.hasOwnProperty, ap = Array.prototype, apsp = ap.splice, isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined', //PS3 indicates loaded and complete, but need to wait for complete //specifically. Sequence is 'loading', 'loaded', execution, // then 'complete'. The UA check is unfortunate, but not sure how //to feature test w/o causing perf issues. readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? /^complete$/ : /^(complete|loaded)$/, defContextName = '_', //Oh the tragedy, detecting opera. See the usage of isOpera for reason. isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', contexts = {}, cfg = {}, globalDefQueue = [], useInteractive = false; function isFunction(it) { return ostring.call(it) === '[object Function]'; } function isArray(it) { return ostring.call(it) === '[object Array]'; } /** * Helper function for iterating over an array. If the func returns * a true value, it will break out of the loop. */ function each(ary, func) { if (ary) { var i; for (i = 0; i < ary.length; i += 1) { if (ary[i] && func(ary[i], i, ary)) { break; } } } } /** * Helper function for iterating over an array backwards. If the func * returns a true value, it will break out of the loop. */ function eachReverse(ary, func) { if (ary) { var i; for (i = ary.length - 1; i > -1; i -= 1) { if (ary[i] && func(ary[i], i, ary)) { break; } } } } function hasProp(obj, prop) { return hasOwn.call(obj, prop); } function getOwn(obj, prop) { return hasProp(obj, prop) && obj[prop]; } /** * Cycles over properties in an object and calls a function for each * property value. If the function returns a truthy value, then the * iteration is stopped. */ function eachProp(obj, func) { var prop; for (prop in obj) { if (hasProp(obj, prop)) { if (func(obj[prop], prop)) { break; } } } } /** * Simple function to mix in properties from source into target, * but only if target does not already have a property of the same name. */ function mixin(target, source, force, deepStringMixin) { if (source) { eachProp(source, function (value, prop) { if (force || !hasProp(target, prop)) { if (deepStringMixin && typeof value === 'object' && value && !isArray(value) && !isFunction(value) && !(value instanceof RegExp)) { if (!target[prop]) { target[prop] = {}; } mixin(target[prop], value, force, deepStringMixin); } else { target[prop] = value; } } }); } return target; } //Similar to Function.prototype.bind, but the 'this' object is specified //first, since it is easier to read/figure out what 'this' will be. function bind(obj, fn) { return function () { return fn.apply(obj, arguments); }; } function scripts() { return document.getElementsByTagName('script'); } function defaultOnError(err) { throw err; } //Allow getting a global that is expressed in //dot notation, like 'a.b.c'. function getGlobal(value) { if (!value) { return value; } var g = global; each(value.split('.'), function (part) { g = g[part]; }); return g; } /** * Constructs an error with a pointer to an URL with more information. * @param {String} id the error ID that maps to an ID on a web page. * @param {String} message human readable error. * @param {Error} [err] the original error, if there is one. * * @returns {Error} */ function makeError(id, msg, err, requireModules) { var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); e.requireType = id; e.requireModules = requireModules; if (err) { e.originalError = err; } return e; } if (typeof define !== 'undefined') { //If a define is already in play via another AMD loader, //do not overwrite. return; } if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { //Do not overwrite an existing requirejs instance. return; } cfg = requirejs; requirejs = undefined; } //Allow for a require config object if (typeof require !== 'undefined' && !isFunction(require)) { //assume it is a config object. cfg = require; require = undefined; } function newContext(contextName) { var inCheckLoaded, Module, context, handlers, checkLoadedTimeoutId, config = { //Defaults. Do not set a default for map //config to speed up normalize(), which //will run faster if there is no default. waitSeconds: 7, baseUrl: './', paths: {}, bundles: {}, pkgs: {}, shim: {}, config: {} }, registry = {}, //registry of just enabled modules, to speed //cycle breaking code when lots of modules //are registered, but not activated. enabledRegistry = {}, undefEvents = {}, defQueue = [], defined = {}, urlFetched = {}, bundlesMap = {}, requireCounter = 1, unnormalizedCounter = 1; /** * Trims the . and .. from an array of path segments. * It will keep a leading path segment if a .. will become * the first path segment, to help with module name lookups, * which act like paths, but can be remapped. But the end result, * all paths that use this function should look normalized. * NOTE: this method MODIFIES the input array. * @param {Array} ary the array of path segments. */ function trimDots(ary) { var i, part; for (i = 0; i < ary.length; i++) { part = ary[i]; if (part === '.') { ary.splice(i, 1); i -= 1; } else if (part === '..') { // If at the start, or previous value is still .., // keep them so that when converted to a path it may // still work when converted to a path, even though // as an ID it is less than ideal. In larger point // releases, may be better to just kick out an error. if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') { continue; } else if (i > 0) { ary.splice(i - 1, 2); i -= 2; } } } } /** * Given a relative module name, like ./something, normalize it to * a real name that can be mapped to a path. * @param {String} name the relative name * @param {String} baseName a real name that the name arg is relative * to. * @param {Boolean} applyMap apply the map config to the value. Should * only be done if this normalization is for a dependency ID. * @returns {String} normalized name */ function normalize(name, baseName, applyMap) { var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, normalizedBaseParts, baseParts = (baseName && baseName.split('/')), map = config.map, starMap = map && map['*']; //Adjust any relative paths. if (name) { name = name.split('/'); lastIndex = name.length - 1; // If wanting node ID compatibility, strip .js from end // of IDs. Have to do this here, and not in nameToUrl // because node allows either .js or non .js to map // to same file. if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); } // Starts with a '.' so need the baseName if (name[0].charAt(0) === '.' && baseParts) { //Convert baseName to array, and lop off the last part, //so that . matches that 'directory' and not name of the baseName's //module. For instance, baseName of 'one/two/three', maps to //'one/two/three.js', but we want the directory, 'one/two' for //this normalization. normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); name = normalizedBaseParts.concat(name); } trimDots(name); name = name.join('/'); } //Apply map config if available. if (applyMap && map && (baseParts || starMap)) { nameParts = name.split('/'); outerLoop: for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join('/'); if (baseParts) { //Find the longest baseName segment match in the config. //So, do joins on the biggest to smallest lengths of baseParts. for (j = baseParts.length; j > 0; j -= 1) { mapValue = getOwn(map, baseParts.slice(0, j).join('/')); //baseName segment has config, find if it has one for //this name. if (mapValue) { mapValue = getOwn(mapValue, nameSegment); if (mapValue) { //Match, update name to the new value. foundMap = mapValue; foundI = i; break outerLoop; } } } } //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { foundStarMap = getOwn(starMap, nameSegment); starI = i; } } if (!foundMap && foundStarMap) { foundMap = foundStarMap; foundI = starI; } if (foundMap) { nameParts.splice(0, foundI, foundMap); name = nameParts.join('/'); } } // If the name points to a package's name, use // the package main instead. pkgMain = getOwn(config.pkgs, name); return pkgMain ? pkgMain : name; } function removeScript(name) { if (isBrowser) { each(scripts(), function (scriptNode) { if (scriptNode.getAttribute('data-requiremodule') === name && scriptNode.getAttribute('data-requirecontext') === context.contextName) { scriptNode.parentNode.removeChild(scriptNode); return true; } }); } } function hasPathFallback(id) { var pathConfig = getOwn(config.paths, id); if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { //Pop off the first array value, since it failed, and //retry pathConfig.shift(); context.require.undef(id); //Custom require that does not do map translation, since //ID is "absolute", already mapped/resolved. context.makeRequire(null, { skipMap: true })([id]); return true; } } //Turns a plugin!resource to [plugin, resource] //with the plugin being undefined if the name //did not have a plugin prefix. function splitPrefix(name) { var prefix, index = name ? name.indexOf('!') : -1; if (index > -1) { prefix = name.substring(0, index); name = name.substring(index + 1, name.length); } return [prefix, name]; } /** * Creates a module mapping that includes plugin prefix, module * name, and path. If parentModuleMap is provided it will * also normalize the name via require.normalize() * * @param {String} name the module name * @param {String} [parentModuleMap] parent module map * for the module name, used to resolve relative names. * @param {Boolean} isNormalized: is the ID already normalized. * This is true if this call is done for a define() module ID. * @param {Boolean} applyMap: apply the map config to the ID. * Should only be true if this map is for a dependency. * * @returns {Object} */ function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { var url, pluginModule, suffix, nameParts, prefix = null, parentName = parentModuleMap ? parentModuleMap.name : null, originalName = name, isDefine = true, normalizedName = ''; //If no name, then it means it is a require call, generate an //internal name. if (!name) { isDefine = false; name = '_@r' + (requireCounter += 1); } nameParts = splitPrefix(name); prefix = nameParts[0]; name = nameParts[1]; if (prefix) { prefix = normalize(prefix, parentName, applyMap); pluginModule = getOwn(defined, prefix); } //Account for relative paths if there is a base name. if (name) { if (prefix) { if (pluginModule && pluginModule.normalize) { //Plugin is loaded, use its normalize method. normalizedName = pluginModule.normalize(name, function (name) { return normalize(name, parentName, applyMap); }); } else { // If nested plugin references, then do not try to // normalize, as it will not normalize correctly. This // places a restriction on resourceIds, and the longer // term solution is not to normalize until plugins are // loaded and all normalizations to allow for async // loading of a loader plugin. But for now, fixes the // common uses. Details in #1131 normalizedName = name.indexOf('!') === -1 ? normalize(name, parentName, applyMap) : name; } } else { //A regular module. normalizedName = normalize(name, parentName, applyMap); //Normalized name may be a plugin ID due to map config //application in normalize. The map config values must //already be normalized, so do not need to redo that part. nameParts = splitPrefix(normalizedName); prefix = nameParts[0]; normalizedName = nameParts[1]; isNormalized = true; url = context.nameToUrl(normalizedName); } } //If the id is a plugin id that cannot be determined if it needs //normalization, stamp it with a unique ID so two matching relative //ids that may conflict can be separate. suffix = prefix && !pluginModule && !isNormalized ? '_unnormalized' + (unnormalizedCounter += 1) : ''; return { prefix: prefix, name: normalizedName, parentMap: parentModuleMap, unnormalized: !!suffix, url: url, originalName: originalName, isDefine: isDefine, id: (prefix ? prefix + '!' + normalizedName : normalizedName) + suffix }; } function getModule(depMap) { var id = depMap.id, mod = getOwn(registry, id); if (!mod) { mod = registry[id] = new context.Module(depMap); } return mod; } function on(depMap, name, fn) { var id = depMap.id, mod = getOwn(registry, id); if (hasProp(defined, id) && (!mod || mod.defineEmitComplete)) { if (name === 'defined') { fn(defined[id]); } } else { mod = getModule(depMap); if (mod.error && name === 'error') { fn(mod.error); } else { mod.on(name, fn); } } } function onError(err, errback) { var ids = err.requireModules, notified = false; if (errback) { errback(err); } else { each(ids, function (id) { var mod = getOwn(registry, id); if (mod) { //Set error on module, so it skips timeout checks. mod.error = err; if (mod.events.error) { notified = true; mod.emit('error', err); } } }); if (!notified) { req.onError(err); } } } /** * Internal method to transfer globalQueue items to this context's * defQueue. */ function takeGlobalQueue() { //Push all the globalDefQueue items into the context's defQueue if (globalDefQueue.length) { //Array splice in the values since the context code has a //local var ref to defQueue, so cannot just reassign the one //on context. apsp.apply(defQueue, [defQueue.length, 0].concat(globalDefQueue)); globalDefQueue = []; } } handlers = { 'require': function (mod) { if (mod.require) { return mod.require; } else { return (mod.require = context.makeRequire(mod.map)); } }, 'exports': function (mod) { mod.usingExports = true; if (mod.map.isDefine) { if (mod.exports) { return (defined[mod.map.id] = mod.exports); } else { return (mod.exports = defined[mod.map.id] = {}); } } }, 'module': function (mod) { if (mod.module) { return mod.module; } else { return (mod.module = { id: mod.map.id, uri: mod.map.url, config: function () { return getOwn(config.config, mod.map.id) || {}; }, exports: mod.exports || (mod.exports = {}) }); } } }; function cleanRegistry(id) { //Clean up machinery used for waiting modules. delete registry[id]; delete enabledRegistry[id]; } function breakCycle(mod, traced, processed) { var id = mod.map.id; if (mod.error) { mod.emit('error', mod.error); } else { traced[id] = true; each(mod.depMaps, function (depMap, i) { var depId = depMap.id, dep = getOwn(registry, depId); //Only force things that have not completed //being defined, so still in the registry, //and only if it has not been matched up //in the module already. if (dep && !mod.depMatched[i] && !processed[depId]) { if (getOwn(traced, depId)) { mod.defineDep(i, defined[depId]); mod.check(); //pass false? } else { breakCycle(dep, traced, processed); } } }); processed[id] = true; } } function checkLoaded() { var err, usingPathFallback, waitInterval = config.waitSeconds * 1000, //It is possible to disable the wait interval by using waitSeconds of 0. expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), noLoads = [], reqCalls = [], stillLoading = false, needCycleCheck = true; //Do not bother if this call was a result of a cycle break. if (inCheckLoaded) { return; } inCheckLoaded = true; //Figure out the state of all the modules. eachProp(enabledRegistry, function (mod) { var map = mod.map, modId = map.id; //Skip things that are not enabled or in error state. if (!mod.enabled) { return; } if (!map.isDefine) { reqCalls.push(mod); } if (!mod.error) { //If the module should be executed, and it has not //been inited and time is up, remember it. if (!mod.inited && expired) { if (hasPathFallback(modId)) { usingPathFallback = true; stillLoading = true; } else { noLoads.push(modId); removeScript(modId); } } else if (!mod.inited && mod.fetched && map.isDefine) { stillLoading = true; if (!map.prefix) { //No reason to keep looking for unfinished //loading. If the only stillLoading is a //plugin resource though, keep going, //because it may be that a plugin resource //is waiting on a non-plugin cycle. return (needCycleCheck = false); } } } }); if (expired && noLoads.length) { //If wait time expired, throw error of unloaded modules. err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); err.contextName = context.contextName; return onError(err); } //Not expired, check for a cycle. if (needCycleCheck) { each(reqCalls, function (mod) { breakCycle(mod, {}, {}); }); } //If still waiting on loads, and the waiting load is something //other than a plugin resource, or there are still outstanding //scripts, then just try back later. if ((!expired || usingPathFallback) && stillLoading) { //Something is still waiting to load. Wait for it, but only //if a timeout is not already in effect. if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { checkLoadedTimeoutId = setTimeout(function () { checkLoadedTimeoutId = 0; checkLoaded(); }, 50); } } inCheckLoaded = false; } Module = function (map) { this.events = getOwn(undefEvents, map.id) || {}; this.map = map; this.shim = getOwn(config.shim, map.id); this.depExports = []; this.depMaps = []; this.depMatched = []; this.pluginMaps = {}; this.depCount = 0; /* this.exports this.factory this.depMaps = [], this.enabled, this.fetched */ }; Module.prototype = { init: function (depMaps, factory, errback, options) { options = options || {}; //Do not do more inits if already done. Can happen if there //are multiple define calls for the same module. That is not //a normal, common case, but it is also not unexpected. if (this.inited) { return; } this.factory = factory; if (errback) { //Register for errors on this module. this.on('error', errback); } else if (this.events.error) { //If no errback already, but there are error listeners //on this module, set up an errback to pass to the deps. errback = bind(this, function (err) { this.emit('error', err); }); } //Do a copy of the dependency array, so that //source inputs are not modified. For example //"shim" deps are passed in here directly, and //doing a direct modification of the depMaps array //would affect that config. this.depMaps = depMaps && depMaps.slice(0); this.errback = errback; //Indicate this module has be initialized this.inited = true; this.ignore = options.ignore; //Could have option to init this module in enabled mode, //or could have been previously marked as enabled. However, //the dependencies are not known until init is called. So //if enabled previously, now trigger dependencies as enabled. if (options.enabled || this.enabled) { //Enable this module and dependencies. //Will call this.check() this.enable(); } else { this.check(); } }, defineDep: function (i, depExports) { //Because of cycles, defined callback for a given //export can be called more than once. if (!this.depMatched[i]) { this.depMatched[i] = true; this.depCount -= 1; this.depExports[i] = depExports; } }, fetch: function () { if (this.fetched) { return; } this.fetched = true; context.startTime = (new Date()).getTime(); var map = this.map; //If the manager is for a plugin managed resource, //ask the plugin to load it now. if (this.shim) { context.makeRequire(this.map, { enableBuildCallback: true })(this.shim.deps || [], bind(this, function () { return map.prefix ? this.callPlugin() : this.load(); })); } else { //Regular dependency. return map.prefix ? this.callPlugin() : this.load(); } }, load: function () { var url = this.map.url; //Regular dependency. if (!urlFetched[url]) { urlFetched[url] = true; context.load(this.map.id, url); } }, /** * Checks if the module is ready to define itself, and if so, * define it. */ check: function () { if (!this.enabled || this.enabling) { return; } var err, cjsModule, id = this.map.id, depExports = this.depExports, exports = this.exports, factory = this.factory; if (!this.inited) { this.fetch(); } else if (this.error) { this.emit('error', this.error); } else if (!this.defining) { //The factory could trigger another require call //that would result in checking this module to //define itself again. If already in the process //of doing that, skip this work. this.defining = true; if (this.depCount < 1 && !this.defined) { if (isFunction(factory)) { //If there is an error listener, favor passing //to that instead of throwing an error. However, //only do it for define()'d modules. require //errbacks should not be called for failures in //their callbacks (#699). However if a global //onError is set, use that. if ((this.events.error && this.map.isDefine) || req.onError !== defaultOnError) { try { exports = context.execCb(id, factory, depExports, exports); } catch (e) { err = e; } } else { exports = context.execCb(id, factory, depExports, exports); } // Favor return value over exports. If node/cjs in play, // then will not have a return value anyway. Favor // module.exports assignment over exports object. if (this.map.isDefine && exports === undefined) { cjsModule = this.module; if (cjsModule) { exports = cjsModule.exports; } else if (this.usingExports) { //exports already set the defined value. exports = this.exports; } } if (err) { err.requireMap = this.map; err.requireModules = this.map.isDefine ? [this.map.id] : null; err.requireType = this.map.isDefine ? 'define' : 'require'; return onError((this.error = err)); } } else { //Just a literal value exports = factory; } this.exports = exports; if (this.map.isDefine && !this.ignore) { defined[id] = exports; if (req.onResourceLoad) { req.onResourceLoad(context, this.map, this.depMaps); } } //Clean up cleanRegistry(id); this.defined = true; } //Finished the define stage. Allow calling check again //to allow define notifications below in the case of a //cycle. this.defining = false; if (this.defined && !this.defineEmitted) { this.defineEmitted = true; this.emit('defined', this.exports); this.defineEmitComplete = true; } } }, callPlugin: function () { var map = this.map, id = map.id, //Map already normalized the prefix. pluginMap = makeModuleMap(map.prefix); //Mark this as a dependency for this plugin, so it //can be traced for cycles. this.depMaps.push(pluginMap); on(pluginMap, 'defined', bind(this, function (plugin) { var load, normalizedMap, normalizedMod, bundleId = getOwn(bundlesMap, this.map.id), name = this.map.name, parentName = this.map.parentMap ? this.map.parentMap.name : null, localRequire = context.makeRequire(map.parentMap, { enableBuildCallback: true }); //If current map is not normalized, wait for that //normalized name to load instead of continuing. if (this.map.unnormalized) { //Normalize the ID if the plugin allows it. if (plugin.normalize) { name = plugin.normalize(name, function (name) { return normalize(name, parentName, true); }) || ''; } //prefix and name should already be normalized, no need //for applying map config again either. normalizedMap = makeModuleMap(map.prefix + '!' + name, this.map.parentMap); on(normalizedMap, 'defined', bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true, ignore: true }); })); normalizedMod = getOwn(registry, normalizedMap.id); if (normalizedMod) { //Mark this as a dependency for this plugin, so it //can be traced for cycles. this.depMaps.push(normalizedMap); if (this.events.error) { normalizedMod.on('error', bind(this, function (err) { this.emit('error', err); })); } normalizedMod.enable(); } return; } //If a paths config, then just load that file instead to //resolve the plugin, as it is built into that paths layer. if (bundleId) { this.map.url = context.nameToUrl(bundleId); this.load(); return; } load = bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true }); }); load.error = bind(this, function (err) { this.inited = true; this.error = err; err.requireModules = [id]; //Remove temp unnormalized modules for this module, //since they will never be resolved otherwise now. eachProp(registry, function (mod) { if (mod.map.id.indexOf(id + '_unnormalized') === 0) { cleanRegistry(mod.map.id); } }); onError(err); }); //Allow plugins to load other code without having to know the //context or how to 'complete' the load. load.fromText = bind(this, function (text, textAlt) { /*jslint evil: true */ var moduleName = map.name, moduleMap = makeModuleMap(moduleName), hasInteractive = useInteractive; //As of 2.1.0, support just passing the text, to reinforce //fromText only being called once per resource. Still //support old style of passing moduleName but discard //that moduleName in favor of the internal ref. if (textAlt) { text = textAlt; } //Turn off interactive script matching for IE for any define //calls in the text, then turn it back on at the end. if (hasInteractive) { useInteractive = false; } //Prime the system by creating a module instance for //it. getModule(moduleMap); //Transfer any config to this other module. if (hasProp(config.config, id)) { config.config[moduleName] = config.config[id]; } try { req.exec(text); } catch (e) { return onError(makeError('fromtexteval', 'fromText eval for ' + id + ' failed: ' + e, e, [id])); } if (hasInteractive) { useInteractive = true; } //Mark this as a dependency for the plugin //resource this.depMaps.push(moduleMap); //Support anonymous modules. context.completeLoad(moduleName); //Bind the value of that module to the value for this //resource ID. localRequire([moduleName], load); }); //Use parentName here since the plugin's name is not reliable, //could be some weird string with no path that actually wants to //reference the parentName's path. plugin.load(map.name, localRequire, load, config); })); context.enable(pluginMap, this); this.pluginMaps[pluginMap.id] = pluginMap; }, enable: function () { enabledRegistry[this.map.id] = this; this.enabled = true; //Set flag mentioning that the module is enabling, //so that immediate calls to the defined callbacks //for dependencies do not trigger inadvertent load //with the depCount still being zero. this.enabling = true; //Enable each dependency each(this.depMaps, bind(this, function (depMap, i) { var id, mod, handler; if (typeof depMap === 'string') { //Dependency needs to be converted to a depMap //and wired up to this module. depMap = makeModuleMap(depMap, (this.map.isDefine ? this.map : this.map.parentMap), false, !this.skipMap); this.depMaps[i] = depMap; handler = getOwn(handlers, depMap.id); if (handler) { this.depExports[i] = handler(this); return; } this.depCount += 1; on(depMap, 'defined', bind(this, function (depExports) { this.defineDep(i, depExports); this.check(); })); if (this.errback) { on(depMap, 'error', bind(this, this.errback)); } } id = depMap.id; mod = registry[id]; //Skip special modules like 'require', 'exports', 'module' //Also, don't call enable if it is already enabled, //important in circular dependency cases. if (!hasProp(handlers, id) && mod && !mod.enabled) { context.enable(depMap, this); } })); //Enable each plugin that is used in //a dependency eachProp(this.pluginMaps, bind(this, function (pluginMap) { var mod = getOwn(registry, pluginMap.id); if (mod && !mod.enabled) { context.enable(pluginMap, this); } })); this.enabling = false; this.check(); }, on: function (name, cb) { var cbs = this.events[name]; if (!cbs) { cbs = this.events[name] = []; } cbs.push(cb); }, emit: function (name, evt) { each(this.events[name], function (cb) { cb(evt); }); if (name === 'error') { //Now that the error handler was triggered, remove //the listeners, since this broken Module instance //can stay around for a while in the registry. delete this.events[name]; } } }; function callGetModule(args) { //Skip modules already defined. if (!hasProp(defined, args[0])) { getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); } } function removeListener(node, func, name, ieName) { //Favor detachEvent because of IE9 //issue, see attachEvent/addEventListener comment elsewhere //in this file. if (node.detachEvent && !isOpera) { //Probably IE. If not it will throw an error, which will be //useful to know. if (ieName) { node.detachEvent(ieName, func); } } else { node.removeEventListener(name, func, false); } } /** * Given an event from a script node, get the requirejs info from it, * and then removes the event listeners on the node. * @param {Event} evt * @returns {Object} */ function getScriptData(evt) { //Using currentTarget instead of target for Firefox 2.0's sake. Not //all old browsers will be supported, but this one was easy enough //to support and still makes sense. var node = evt.currentTarget || evt.srcElement; //Remove the listeners once here. removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); removeListener(node, context.onScriptError, 'error'); return { node: node, id: node && node.getAttribute('data-requiremodule') }; } function intakeDefines() { var args; //Any defined modules in the global queue, intake them now. takeGlobalQueue(); //Make sure any remaining defQueue items get properly processed. while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); } else { //args are id, deps, factory. Should be normalized by the //define() function. callGetModule(args); } } } context = { config: config, contextName: contextName, registry: registry, defined: defined, urlFetched: urlFetched, defQueue: defQueue, Module: Module, makeModuleMap: makeModuleMap, nextTick: req.nextTick, onError: onError, /** * Set a configuration for the context. * @param {Object} cfg config object to integrate. */ configure: function (cfg) { //Make sure the baseUrl ends in a slash. if (cfg.baseUrl) { if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { cfg.baseUrl += '/'; } } //Save off the paths since they require special processing, //they are additive. var shim = config.shim, objs = { paths: true, bundles: true, config: true, map: true }; eachProp(cfg, function (value, prop) { if (objs[prop]) { if (!config[prop]) { config[prop] = {}; } mixin(config[prop], value, true, true); } else { config[prop] = value; } }); //Reverse map the bundles if (cfg.bundles) { eachProp(cfg.bundles, function (value, prop) { each(value, function (v) { if (v !== prop) { bundlesMap[v] = prop; } }); }); } //Merge shim if (cfg.shim) { eachProp(cfg.shim, function (value, id) { //Normalize the structure if (isArray(value)) { value = { deps: value }; } if ((value.exports || value.init) && !value.exportsFn) { value.exportsFn = context.makeShimExports(value); } shim[id] = value; }); config.shim = shim; } //Adjust packages if necessary. if (cfg.packages) { each(cfg.packages, function (pkgObj) { var location, name; pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; name = pkgObj.name; location = pkgObj.location; if (location) { config.paths[name] = pkgObj.location; } //Save pointer to main module ID for pkg name. //Remove leading dot in main, so main paths are normalized, //and remove any trailing .js, since different package //envs have different conventions: some use a module name, //some use a file name. config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') .replace(currDirRegExp, '') .replace(jsSuffixRegExp, ''); }); } //If there are any "waiting to execute" modules in the registry, //update the maps for them, since their info, like URLs to load, //may have changed. eachProp(registry, function (mod, id) { //If module already has init called, since it is too //late to modify them, and ignore unnormalized ones //since they are transient. if (!mod.inited && !mod.map.unnormalized) { mod.map = makeModuleMap(id); } }); //If a deps array or a config callback is specified, then call //require with those args. This is useful when require is defined as a //config object before require.js is loaded. if (cfg.deps || cfg.callback) { context.require(cfg.deps || [], cfg.callback); } }, makeShimExports: function (value) { function fn() { var ret; if (value.init) { ret = value.init.apply(global, arguments); } return ret || (value.exports && getGlobal(value.exports)); } return fn; }, makeRequire: function (relMap, options) { options = options || {}; function localRequire(deps, callback, errback) { var id, map, requireMod; if (options.enableBuildCallback && callback && isFunction(callback)) { callback.__requireJsBuild = true; } if (typeof deps === 'string') { if (isFunction(callback)) { //Invalid call return onError(makeError('requireargs', 'Invalid require call'), errback); } //If require|exports|module are requested, get the //value for them from the special handlers. Caveat: //this only works while module is being defined. if (relMap && hasProp(handlers, deps)) { return handlers[deps](registry[relMap.id]); } //Synchronous access to one module. If require.get is //available (as in the Node adapter), prefer that. if (req.get) { return req.get(context, deps, relMap, localRequire); } //Normalize module name, if it contains . or .. map = makeModuleMap(deps, relMap, false, true); id = map.id; if (!hasProp(defined, id)) { return onError(makeError('notloaded', 'Module name "' + id + '" has not been loaded yet for context: ' + contextName + (relMap ? '' : '. Use require([])'))); } return defined[id]; } //Grab defines waiting in the global queue. intakeDefines(); //Mark all the dependencies as needing to be loaded. context.nextTick(function () { //Some defines could have been added since the //require call, collect them. intakeDefines(); requireMod = getModule(makeModuleMap(null, relMap)); //Store if map config should be applied to this require //call for dependencies. requireMod.skipMap = options.skipMap; requireMod.init(deps, callback, errback, { enabled: true }); checkLoaded(); }); return localRequire; } mixin(localRequire, { isBrowser: isBrowser, /** * Converts a module name + .extension into an URL path. * *Requires* the use of a module name. It does not support using * plain URLs like nameToUrl. */ toUrl: function (moduleNamePlusExt) { var ext, index = moduleNamePlusExt.lastIndexOf('.'), segment = moduleNamePlusExt.split('/')[0], isRelative = segment === '.' || segment === '..'; //Have a file extension alias, and it is not the //dots from a relative path. if (index !== -1 && (!isRelative || index > 1)) { ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); moduleNamePlusExt = moduleNamePlusExt.substring(0, index); } return context.nameToUrl(normalize(moduleNamePlusExt, relMap && relMap.id, true), ext, true); }, defined: function (id) { return hasProp(defined, makeModuleMap(id, relMap, false, true).id); }, specified: function (id) { id = makeModuleMap(id, relMap, false, true).id; return hasProp(defined, id) || hasProp(registry, id); } }); //Only allow undef on top level require calls if (!relMap) { localRequire.undef = function (id) { //Bind any waiting define() calls to this context, //fix for #408 takeGlobalQueue(); var map = makeModuleMap(id, relMap, true), mod = getOwn(registry, id); removeScript(id); delete defined[id]; delete urlFetched[map.url]; delete undefEvents[id]; //Clean queued defines too. Go backwards //in array so that the splices do not //mess up the iteration. eachReverse(defQueue, function(args, i) { if(args[0] === id) { defQueue.splice(i, 1); } }); if (mod) { //Hold on to listeners in case the //module will be attempted to be reloaded //using a different config. if (mod.events.defined) { undefEvents[id] = mod.events; } cleanRegistry(id); } }; } return localRequire; }, /** * Called to enable a module if it is still in the registry * awaiting enablement. A second arg, parent, the parent module, * is passed in for context, when this method is overridden by * the optimizer. Not shown here to keep code compact. */ enable: function (depMap) { var mod = getOwn(registry, depMap.id); if (mod) { getModule(depMap).enable(); } }, /** * Internal method used by environment adapters to complete a load event. * A load event could be a script load or just a load pass from a synchronous * load call. * @param {String} moduleName the name of the module to potentially complete. */ completeLoad: function (moduleName) { var found, args, mod, shim = getOwn(config.shim, moduleName) || {}, shExports = shim.exports; takeGlobalQueue(); while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { args[0] = moduleName; //If already found an anonymous module and bound it //to this name, then this is some other anon module //waiting for its completeLoad to fire. if (found) { break; } found = true; } else if (args[0] === moduleName) { //Found matching define call for this script! found = true; } callGetModule(args); } //Do this after the cycle of callGetModule in case the result //of those calls/init calls changes the registry. mod = getOwn(registry, moduleName); if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { if (hasPathFallback(moduleName)) { return; } else { return onError(makeError('nodefine', 'No define call for ' + moduleName, null, [moduleName])); } } else { //A script that does not call define(), so just simulate //the call for it. callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); } } checkLoaded(); }, /** * Converts a module name to a file path. Supports cases where * moduleName may actually be just an URL. * Note that it **does not** call normalize on the moduleName, * it is assumed to have already been normalized. This is an * internal API, not a public one. Use toUrl for the public API. */ nameToUrl: function (moduleName, ext, skipExt) { var paths, syms, i, parentModule, url, parentPath, bundleId, pkgMain = getOwn(config.pkgs, moduleName); if (pkgMain) { moduleName = pkgMain; } bundleId = getOwn(bundlesMap, moduleName); if (bundleId) { return context.nameToUrl(bundleId, ext, skipExt); } //If a colon is in the URL, it indicates a protocol is used and it is just //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) //or ends with .js, then assume the user meant to use an url and not a module id. //The slash is important for protocol-less URLs as well as full paths. if (req.jsExtRegExp.test(moduleName)) { //Just a plain path, not module name lookup, so just return it. //Add extension if it is included. This is a bit wonky, only non-.js things pass //an extension, this method probably needs to be reworked. url = moduleName + (ext || ''); } else { //A module that needs to be converted to a path. paths = config.paths; syms = moduleName.split('/'); //For each module name segment, see if there is a path //registered for it. Start with most specific name //and work up from it. for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); parentPath = getOwn(paths, parentModule); if (parentPath) { //If an array, it means there are a few choices, //Choose the one that is desired if (isArray(parentPath)) { parentPath = parentPath[0]; } syms.splice(0, i, parentPath); break; } } //Join the path parts together, then figure out if baseUrl is needed. url = syms.join('/'); url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; } return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) : url; }, //Delegates to req.load. Broken out as a separate function to //allow overriding in the optimizer. load: function (id, url) { req.load(context, id, url); }, /** * Executes a module callback function. Broken out as a separate function * solely to allow the build system to sequence the files in the built * layer in the right sequence. * * @private */ execCb: function (name, callback, args, exports) { return callback.apply(exports, args); }, /** * callback for script loads, used to check status of loading. * * @param {Event} evt the event from the browser for the script * that was loaded. */ onScriptLoad: function (evt) { //Using currentTarget instead of target for Firefox 2.0's sake. Not //all old browsers will be supported, but this one was easy enough //to support and still makes sense. if (evt.type === 'load' || (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { //Reset interactive script so a script node is not held onto for //to long. interactiveScript = null; //Pull out the name of the module and the context. var data = getScriptData(evt); context.completeLoad(data.id); } }, /** * Callback for script errors. */ onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); } } }; context.require = context.makeRequire(); return context; } /** * Main entry point. * * If the only argument to require is a string, then the module that * is represented by that string is fetched for the appropriate context. * * If the first argument is an array, then it will be treated as an array * of dependency string names to fetch. An optional function callback can * be specified to execute when all of those dependencies are available. * * Make a local req variable to help Caja compliance (it assumes things * on a require that are not standardized), and to give a short * name for minification/local scope use. */ req = requirejs = function (deps, callback, errback, optional) { //Find the right context, use default var context, config, contextName = defContextName; // Determine if have config object in the call. if (!isArray(deps) && typeof deps !== 'string') { // deps is a config object config = deps; if (isArray(callback)) { // Adjust args if there are dependencies deps = callback; callback = errback; errback = optional; } else { deps = []; } } if (config && config.context) { contextName = config.context; } context = getOwn(contexts, contextName); if (!context) { context = contexts[contextName] = req.s.newContext(contextName); } if (config) { context.configure(config); } return context.require(deps, callback, errback); }; /** * Support require.config() to make it easier to cooperate with other * AMD loaders on globally agreed names. */ req.config = function (config) { return req(config); }; /** * Execute something after the current tick * of the event loop. Override for other envs * that have a better solution than setTimeout. * @param {Function} fn function to execute later. */ req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { setTimeout(fn, 4); } : function (fn) { fn(); }; /** * Export require as a global, but only if it does not already exist. */ if (!require) { require = req; } req.version = version; //Used to filter out dependencies that are already paths. req.jsExtRegExp = /^\/|:|\?|\.js$/; req.isBrowser = isBrowser; s = req.s = { contexts: contexts, newContext: newContext }; //Create default context. req({}); //Exports some context-sensitive methods on global require. each([ 'toUrl', 'undef', 'defined', 'specified' ], function (prop) { //Reference from contexts instead of early binding to default context, //so that during builds, the latest instance of the default context //with its config gets used. req[prop] = function () { var ctx = contexts[defContextName]; return ctx.require[prop].apply(ctx, arguments); }; }); if (isBrowser) { head = s.head = document.getElementsByTagName('head')[0]; //If BASE tag is in play, using appendChild is a problem for IE6. //When that browser dies, this can be removed. Details in this jQuery bug: //http://dev.jquery.com/ticket/2709 baseElement = document.getElementsByTagName('base')[0]; if (baseElement) { head = s.head = baseElement.parentNode; } } /** * Any errors that require explicitly generates will be passed to this * function. Intercept/override it if you want custom error handling. * @param {Error} err the error object. */ req.onError = defaultOnError; /** * Creates the node for the load command. Only used in browser envs. */ req.createNode = function (config, moduleName, url) { var node = config.xhtml ? document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : document.createElement('script'); node.type = config.scriptType || 'text/javascript'; node.charset = 'utf-8'; node.async = true; return node; }; /** * Does the request to load a module for the browser case. * Make this a separate function to allow other environments * to override it. * * @param {Object} context the require context to find state. * @param {String} moduleName the name of the module. * @param {Object} url the URL to the module. */ req.load = function (context, moduleName, url) { var config = (context && context.config) || {}, node; if (isBrowser) { //In the browser so use a script tag node = req.createNode(config, moduleName, url); node.setAttribute('data-requirecontext', context.contextName); node.setAttribute('data-requiremodule', moduleName); //Set up load listener. Test attachEvent first because IE9 has //a subtle issue in its addEventListener and script onload firings //that do not match the behavior of all other browsers with //addEventListener support, which fire the onload event for a //script right after the script execution. See: //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution //UNFORTUNATELY Opera implements attachEvent but does not follow the script //script execution mode. if (node.attachEvent && //Check if node.attachEvent is artificially added by custom script or //natively supported by browser //read https://github.com/jrburke/requirejs/issues/187 //if we can NOT find [native code] then it must NOT natively supported. //in IE8, node.attachEvent does not have toString() //Note the test for "[native code" with no closing brace, see: //https://github.com/jrburke/requirejs/issues/273 !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera) { //Probably IE. IE (at least 6-8) do not fire //script onload right after executing the script, so //we cannot tie the anonymous define call to a name. //However, IE reports the script as being in 'interactive' //readyState at the time of the define call. useInteractive = true; node.attachEvent('onreadystatechange', context.onScriptLoad); //It would be great to add an error handler here to catch //404s in IE9+. However, onreadystatechange will fire before //the error handler, so that does not help. If addEventListener //is used, then IE will fire error before load, but we cannot //use that pathway given the connect.microsoft.com issue //mentioned above about not doing the 'script execute, //then fire the script load event listener before execute //next script' that other browsers do. //Best hope: IE10 fixes the issues, //and then destroys all installs of IE 6-9. //node.attachEvent('onerror', context.onScriptError); } else { node.addEventListener('load', context.onScriptLoad, false); node.addEventListener('error', context.onScriptError, false); } node.src = url; //For some cache cases in IE 6-8, the script executes before the end //of the appendChild execution, so to tie an anonymous define //call to the module name (which is stored on the node), hold on //to a reference to this node, but clear after the DOM insertion. currentlyAddingScript = node; if (baseElement) { head.insertBefore(node, baseElement); } else { head.appendChild(node); } currentlyAddingScript = null; return node; } else if (isWebWorker) { try { //In a web worker, use importScripts. This is not a very //efficient use of importScripts, importScripts will block until //its script is downloaded and evaluated. However, if web workers //are in play, the expectation that a build has been done so that //only one script needs to be loaded anyway. This may need to be //reevaluated if other use cases become common. importScripts(url); //Account for anonymous modules context.completeLoad(moduleName); } catch (e) { context.onError(makeError('importscripts', 'importScripts failed for ' + moduleName + ' at ' + url, e, [moduleName])); } } }; function getInteractiveScript() { if (interactiveScript && interactiveScript.readyState === 'interactive') { return interactiveScript; } eachReverse(scripts(), function (script) { if (script.readyState === 'interactive') { return (interactiveScript = script); } }); return interactiveScript; } //Look for a data-main script attribute, which could also adjust the baseUrl. if (isBrowser && !cfg.skipDataMain) { //Figure out baseUrl. Get it from the script tag with require.js in it. eachReverse(scripts(), function (script) { //Set the 'head' where we can append children by //using the script's parent. if (!head) { head = script.parentNode; } //Look for a data-main attribute to set main script for the page //to load. If it is there, the path to data main becomes the //baseUrl, if it is not already set. dataMain = script.getAttribute('data-main'); if (dataMain) { //Preserve dataMain in case it is a path (i.e. contains '?') mainScript = dataMain; //Set final baseUrl if there is not already an explicit one. if (!cfg.baseUrl) { //Pull off the directory of data-main for use as the //baseUrl. src = mainScript.split('/'); mainScript = src.pop(); subPath = src.length ? src.join('/') + '/' : './'; cfg.baseUrl = subPath; } //Strip off any trailing .js since mainScript is now //like a module name. mainScript = mainScript.replace(jsSuffixRegExp, ''); //If mainScript is still a path, fall back to dataMain if (req.jsExtRegExp.test(mainScript)) { mainScript = dataMain; } //Put the data-main script in the files to load. cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; return true; } }); } /** * The function that handles definitions of modules. Differs from * require() in that a string for the module should be the first argument, * and the function to execute after dependencies are loaded should * return a value to define the module corresponding to the first argument's * name. */ define = function (name, deps, callback) { var node, context; //Allow for anonymous modules if (typeof name !== 'string') { //Adjust args appropriately callback = deps; deps = name; name = null; } //This module may not have dependencies if (!isArray(deps)) { callback = deps; deps = null; } //If no name, and callback is a function, then figure out if it a //CommonJS thing with dependencies. if (!deps && isFunction(callback)) { deps = []; //Remove comments from the callback string, //look for require calls, and pull them into the dependencies, //but only if there are function args. if (callback.length) { callback .toString() .replace(commentRegExp, '') .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); //May be a CommonJS thing even without require calls, but still //could use exports, and module. Avoid doing exports and module //work though if it just needs require. //REQUIRES the function to expect the CommonJS variables in the //order listed below. deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); } } //If in IE 6-8 and hit an anonymous define() call, do the interactive //work. if (useInteractive) { node = currentlyAddingScript || getInteractiveScript(); if (node) { if (!name) { name = node.getAttribute('data-requiremodule'); } context = contexts[node.getAttribute('data-requirecontext')]; } } //Always save off evaluating the def call until the script onload handler. //This allows multiple modules to be in a file without prematurely //tracing dependencies, and allows for anonymous module support, //where the module name is not known until the script onload event //occurs. If no context, use the global queue, and get it processed //in the onscript load callback. (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); }; define.amd = { jQuery: true }; /** * Executes the text. Normally just uses eval, but can be modified * to use a better, environment-specific call. Only used for transpiling * loader plugins, not for plain JS modules. * @param {String} text the text to execute/evaluate. */ req.exec = function (text) { /*jslint evil: true */ return eval(text); }; //Set up with config info. req(cfg); }(this)); ================================================ FILE: assets/js/lib/text.js ================================================ /** * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/requirejs/text for details */ /*jslint regexp: true */ /*global require, XMLHttpRequest, ActiveXObject, define, window, process, Packages, java, location, Components, FileUtils */ define(['module'], function (module) { 'use strict'; var text, fs, Cc, Ci, xpcIsWindows, progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, hasLocation = typeof location !== 'undefined' && location.href, defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), defaultHostName = hasLocation && location.hostname, defaultPort = hasLocation && (location.port || undefined), buildMap = {}, masterConfig = (module.config && module.config()) || {}; text = { version: '2.0.12', strip: function (content) { //Strips declarations so that external SVG and XML //documents can be added to a document without worry. Also, if the string //is an HTML document, only the part inside the body tag is returned. if (content) { content = content.replace(xmlRegExp, ""); var matches = content.match(bodyRegExp); if (matches) { content = matches[1]; } } else { content = ""; } return content; }, jsEscape: function (content) { return content.replace(/(['\\])/g, '\\$1') .replace(/[\f]/g, "\\f") .replace(/[\b]/g, "\\b") .replace(/[\n]/g, "\\n") .replace(/[\t]/g, "\\t") .replace(/[\r]/g, "\\r") .replace(/[\u2028]/g, "\\u2028") .replace(/[\u2029]/g, "\\u2029"); }, createXhr: masterConfig.createXhr || function () { //Would love to dump the ActiveX crap in here. Need IE 6 to die first. var xhr, i, progId; if (typeof XMLHttpRequest !== "undefined") { return new XMLHttpRequest(); } else if (typeof ActiveXObject !== "undefined") { for (i = 0; i < 3; i += 1) { progId = progIds[i]; try { xhr = new ActiveXObject(progId); } catch (e) {} if (xhr) { progIds = [progId]; // so faster next time break; } } } return xhr; }, /** * Parses a resource name into its component parts. Resource names * look like: module/name.ext!strip, where the !strip part is * optional. * @param {String} name the resource name * @returns {Object} with properties "moduleName", "ext" and "strip" * where strip is a boolean. */ parseName: function (name) { var modName, ext, temp, strip = false, index = name.indexOf("."), isRelative = name.indexOf('./') === 0 || name.indexOf('../') === 0; if (index !== -1 && (!isRelative || index > 1)) { modName = name.substring(0, index); ext = name.substring(index + 1, name.length); } else { modName = name; } temp = ext || modName; index = temp.indexOf("!"); if (index !== -1) { //Pull off the strip arg. strip = temp.substring(index + 1) === "strip"; temp = temp.substring(0, index); if (ext) { ext = temp; } else { modName = temp; } } return { moduleName: modName, ext: ext, strip: strip }; }, xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, /** * Is an URL on another domain. Only works for browser use, returns * false in non-browser environments. Only used to know if an * optimized .js version of a text resource should be loaded * instead. * @param {String} url * @returns Boolean */ useXhr: function (url, protocol, hostname, port) { var uProtocol, uHostName, uPort, match = text.xdRegExp.exec(url); if (!match) { return true; } uProtocol = match[2]; uHostName = match[3]; uHostName = uHostName.split(':'); uPort = uHostName[1]; uHostName = uHostName[0]; return (!uProtocol || uProtocol === protocol) && (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && ((!uPort && !uHostName) || uPort === port); }, finishLoad: function (name, strip, content, onLoad) { content = strip ? text.strip(content) : content; if (masterConfig.isBuild) { buildMap[name] = content; } onLoad(content); }, load: function (name, req, onLoad, config) { //Name has format: some.module.filext!strip //The strip part is optional. //if strip is present, then that means only get the string contents //inside a body tag in an HTML string. For XML/SVG content it means //removing the declarations so the content can be inserted //into the current doc without problems. // Do not bother with the work if a build and text will // not be inlined. if (config && config.isBuild && !config.inlineText) { onLoad(); return; } masterConfig.isBuild = config && config.isBuild; var parsed = text.parseName(name), nonStripName = parsed.moduleName + (parsed.ext ? '.' + parsed.ext : ''), url = req.toUrl(nonStripName), useXhr = (masterConfig.useXhr) || text.useXhr; // Do not load if it is an empty: url if (url.indexOf('empty:') === 0) { onLoad(); return; } //Load the text. Use XHR if possible and in a browser. if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { text.get(url, function (content) { text.finishLoad(name, parsed.strip, content, onLoad); }, function (err) { if (onLoad.error) { onLoad.error(err); } }); } else { //Need to fetch the resource across domains. Assume //the resource has been optimized into a JS module. Fetch //by the module name + extension, but do not include the //!strip part to avoid file system issues. req([nonStripName], function (content) { text.finishLoad(parsed.moduleName + '.' + parsed.ext, parsed.strip, content, onLoad); }); } }, write: function (pluginName, moduleName, write, config) { if (buildMap.hasOwnProperty(moduleName)) { var content = text.jsEscape(buildMap[moduleName]); write.asModule(pluginName + "!" + moduleName, "define(function () { return '" + content + "';});\n"); } }, writeFile: function (pluginName, moduleName, req, write, config) { var parsed = text.parseName(moduleName), extPart = parsed.ext ? '.' + parsed.ext : '', nonStripName = parsed.moduleName + extPart, //Use a '.js' file name so that it indicates it is a //script that can be loaded across domains. fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; //Leverage own load() method to load plugin value, but only //write out values that do not have the strip argument, //to avoid any potential issues with ! in file names. text.load(nonStripName, req, function (value) { //Use own write() method to construct full module value. //But need to create shell that translates writeFile's //write() to the right interface. var textWrite = function (contents) { return write(fileName, contents); }; textWrite.asModule = function (moduleName, contents) { return write.asModule(moduleName, fileName, contents); }; text.write(pluginName, nonStripName, textWrite, config); }, config); } }; if (masterConfig.env === 'node' || (!masterConfig.env && typeof process !== "undefined" && process.versions && !!process.versions.node && !process.versions['node-webkit'])) { //Using special require.nodeRequire, something added by r.js. fs = require.nodeRequire('fs'); text.get = function (url, callback, errback) { try { var file = fs.readFileSync(url, 'utf8'); //Remove BOM (Byte Mark Order) from utf8 files if it is there. if (file.indexOf('\uFEFF') === 0) { file = file.substring(1); } callback(file); } catch (e) { if (errback) { errback(e); } } }; } else if (masterConfig.env === 'xhr' || (!masterConfig.env && text.createXhr())) { text.get = function (url, callback, errback, headers) { var xhr = text.createXhr(), header; xhr.open('GET', url, true); //Allow plugins direct access to xhr headers if (headers) { for (header in headers) { if (headers.hasOwnProperty(header)) { xhr.setRequestHeader(header.toLowerCase(), headers[header]); } } } //Allow overrides specified in config if (masterConfig.onXhr) { masterConfig.onXhr(xhr, url); } xhr.onreadystatechange = function (evt) { var status, err; //Do not explicitly handle errors, those should be //visible via console output in the browser. if (xhr.readyState === 4) { status = xhr.status || 0; if (status > 399 && status < 600) { //An http 4xx or 5xx error. Signal an error. err = new Error(url + ' HTTP status: ' + status); err.xhr = xhr; if (errback) { errback(err); } } else { callback(xhr.responseText); } if (masterConfig.onXhrComplete) { masterConfig.onXhrComplete(xhr, url); } } }; xhr.send(null); }; } else if (masterConfig.env === 'rhino' || (!masterConfig.env && typeof Packages !== 'undefined' && typeof java !== 'undefined')) { //Why Java, why is this so awkward? text.get = function (url, callback) { var stringBuffer, line, encoding = "utf-8", file = new java.io.File(url), lineSeparator = java.lang.System.getProperty("line.separator"), input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), content = ''; try { stringBuffer = new java.lang.StringBuffer(); line = input.readLine(); // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 // http://www.unicode.org/faq/utf_bom.html // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 if (line && line.length() && line.charAt(0) === 0xfeff) { // Eat the BOM, since we've already found the encoding on this file, // and we plan to concatenating this buffer with others; the BOM should // only appear at the top of a file. line = line.substring(1); } if (line !== null) { stringBuffer.append(line); } while ((line = input.readLine()) !== null) { stringBuffer.append(lineSeparator); stringBuffer.append(line); } //Make sure we return a JavaScript string and not a Java string. content = String(stringBuffer.toString()); //String } finally { input.close(); } callback(content); }; } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && typeof Components !== 'undefined' && Components.classes && Components.interfaces)) { //Avert your gaze! Cc = Components.classes; Ci = Components.interfaces; Components.utils['import']('resource://gre/modules/FileUtils.jsm'); xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); text.get = function (url, callback) { var inStream, convertStream, fileObj, readData = {}; if (xpcIsWindows) { url = url.replace(/\//g, '\\'); } fileObj = new FileUtils.File(url); //XPCOM, you so crazy try { inStream = Cc['@mozilla.org/network/file-input-stream;1'] .createInstance(Ci.nsIFileInputStream); inStream.init(fileObj, 1, 0, false); convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] .createInstance(Ci.nsIConverterInputStream); convertStream.init(inStream, "utf-8", inStream.available(), Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); convertStream.readString(inStream.available(), readData); convertStream.close(); inStream.close(); callback(readData.value); } catch (e) { throw new Error((fileObj && fileObj.path || '') + ': ' + e); } }; } return text; }); ================================================ FILE: assets/js/load-options.js ================================================ /* * * Assets options * * */ define([ 'jquery', 'text!/api/options', 'sourceModules/inlineOptions' ], function($, options, inlineOptions) { // Default + User options merged built. Built from Grunt. var sourceOptions = JSON.parse(options); // Override with from page inline options $.extend(true, sourceOptions, inlineOptions); return sourceOptions; }); ================================================ FILE: assets/js/modules/auth.js ================================================ define([ 'jquery', 'sourceModules/module' ], function($, Module) { 'use strict'; /** * @Object default module option values */ var defaults = { 'storageKey': 'sourcejsUser', 'defaultAvatarURL': '/source/assets/i/unknown.gif', 'classes': { 'controlsWrapper': 'source_login', 'loginButton': 'source_login-button', 'avatar': 'source_login-avatar', 'anonymous': 'anonymous', 'hook': 'js-hook' }, 'labels': { 'login': 'Login', 'logout': 'Logout' } }; /** * @module Auth - basic GitHub authorization module. * * @constructor * * @param [Object] config - auth inline configuration set of options * * @param [Object] config.target - jquery domElement which goes to be auth controlls container * * @param [Object] config.options - options set, which allows to define component configuration. */ function Auth(config) { var _this = this; this.conf = $.extend(true, {}, defaults, config.options, this.options.modulesOptions.auth ); this.target = config.target || $(this.conf.classes.hook); if(!$(this.target).hasClass(this.conf.classes.controlsWrapper)) { $(this.target).addClass(this.conf.classes.controlsWrapper); } this.popup; $(function() { _this.init(); }); } Auth.prototype = Module.createInstance(); Auth.prototype.constructor = Auth; Auth.renderers = { 'avatar': function() { var hasAvatar = this.user && this.user.avatar_url; this.target.append($([ '' ].join('') )); }, 'loginButton': function() { this.target.append($([ '
', (this.user && this.user.id ? this.conf.labels.logout : this.conf.labels.login), '
' ].join('') )); } }; /** * @method Auth.login. * This function initiates logging in process and creates github login popup. */ Auth.prototype.login = function() { this.popup = open('/auth/stub', 'popup', 'width=1015,height=500'); }; /** * @method Auth.logout. * This method removes existed user entity and refreshes control. */ Auth.prototype.logout = function() { localStorage.removeItem(this.conf.storageKey); this.render(); }; /** * @method Auth.isLoginned User state getter. * * @returns {Boolean} isLoginned. It returns true if user is loginned. */ Auth.prototype.isLoginned = function() { return !!localStorage[this.conf.storageKey]; }; /** * @method Auth.getUser GitHub user entity getter. * * @returns {Object} user || null. * If user is loginned it returns user object and null in other case. */ Auth.prototype.getUser = function() { return this.isLoginned() ? JSON.parse(localStorage.getItem(this.conf.storageKey)) : null; }; Auth.prototype.init = function() { window.sourcejs = window.sourcejs || {}; var self = window.sourcejs.__auth = this; this.render(); $('body').on('click', '.' + this.conf.classes.loginButton, function(e) { e.preventDefault(); if (self.isLoginned()) { self.logout(); } else { self.login(); } }); }; Auth.prototype.render = function() { var user = this.getUser(); var self = this; this.target.html(''); Object.keys(Auth.renderers).forEach(function(name) { Auth.renderers[name].call({ 'target': self.target, 'user': user, 'conf': self.conf }); }); }; Auth.prototype.done = function(user) { this.popup.close(); localStorage.setItem(this.conf.storageKey, JSON.stringify(user)); this.render(); }; return Auth; }); ================================================ FILE: assets/js/modules/browser.js ================================================ "use strict"; define(["sourceLib/jquery.mb.browser"], function() { //Browser context classes var browserClasses = { msie: "ie", opera: "opera", mozilla: "mozilla", webkit: "webkit" }; var classString = ""; if ($.browser.msie) { classString += browserClasses.msie; classString += " " + browserClasses.msie + $.browser.version; } else if ($.browser.opera) { classString += browserClasses.opera; classString += " " + browserClasses.opera + $.browser.version; } else if ($.browser.mozilla) { classString += browserClasses.mozilla; classString += " " + browserClasses.mozilla + $.browser.version; } else if ($.browser.webkit) { classString += browserClasses.webkit; classString += " " + browserClasses.webkit + $.browser.version; } else { classString += $.browser.name.toLowerCase(); } $('html').addClass(classString); return $.browser; }); ================================================ FILE: assets/js/modules/clarifyInSpec.js ================================================ /* * * Clarify helpers on spec page * * @author Robert Haritonov http://rhr.me * * */ 'use strict'; define([ 'jquery', 'sourceModules/module', 'sourceModules/sections', 'sourceModules/specDecorations' ], function($, module, sections, specDecorations) { function ClarifyInSpec() { var _this = this; this.options.modulesOptions.clarifyInSpec = $.extend({}, { linkTitle: 'Open this example in Clarify' }, this.options.modulesOptions.clarifyInSpec); this.moduleOptions = this.options.modulesOptions.clarifyInSpec; $(function(){ _this.drawClarifyLinks(); }); } ClarifyInSpec.prototype = module.createInstance(); ClarifyInSpec.prototype.constructor = ClarifyInSpec; ClarifyInSpec.prototype.drawClarifyLinks = function(){ var _this = this; var prependHTML = function($el, id){ $el.prepend(''); }; for (var i = 0; i < sections.getQuantity(); i++) { var section = sections.getSections()[i]; var $sectionHeader = section.headerElement; if (_this.haveSectionsInside($sectionHeader)) prependHTML($sectionHeader, section.id); if (section.subHeaderElements !== undefined) { for (var j = 0; j < section.subHeaderElements.length; j++) { var $sectionSubHeader = section.subHeaderElements[j]; var subHeaderID = $sectionSubHeader.attr('id'); if (subHeaderID && _this.haveSectionsInside($sectionSubHeader)) { prependHTML($sectionSubHeader, subHeaderID); } } } } }; ClarifyInSpec.prototype.haveSectionsInside = function($element){ var _this = this; var response = false; var nodes = $element.closest("*").nextUntil("h2,h3,h4"); nodes.each(function(){ if ($(this).hasClass(_this.options.exampleSectionClass)) { response = true; return false; } }); return response; }; return new ClarifyInSpec(); }); ================================================ FILE: assets/js/modules/codeSource.js ================================================ /* * * View demo-examples source code * * */ define([ "jquery", "source/load-options", "sourceModules/utils", "sourceModules/css", "sourceModules/browser", "sourceLib/codeFormat", "sourceModules/innerNavigation", "sourceLib/prism/prism" ], function($, options, utils, Css, browser, codeFormat, innerNavigation) { 'use strict'; if (!(browser.msie && parseInt(browser.version, 10) < 9)) { // and if not ie < 9 $(document).ready(function() { var SourceCode = 'source_source-code'; var SourceCodeShow = SourceCode + '__show'; var SourceCodeMin = SourceCode + '__min'; var SourceCodeStatic = SourceCode + '__static'; var SourceCodeCnt = 'source_source-code_cnt'; var SourceCodeToggle = 'source_source-code_toggle'; var SourceCodeToggleCSS = SourceCodeToggle + '__css'; var SourceCodeToggleHTML = SourceCodeToggle + '__html'; var SourceCodeToggleJS = SourceCodeToggle + '__js'; var SourceCodeToggleAll = 'source_source-code_toggle-all'; var SourceCodeToggleAllHide = SourceCodeToggleAll + '__hide'; var RES_HIDE_CODE = 'Hide'; var RES_SHOW_CODE = 'Show'; var RES_CODE = 'code'; var RES_TOGGLER_SHOW_CODE = 'Show source code'; var prepared = false; var onlyStatic = true; //Add HTML source code container before each example without it $('.source_example').each(function () { var _this = $(this); //
...
is the wrapper for prism.js plugin if ( !_this.prev().hasClass('src-html') ) { _this.before(''); } }); var wrapTags = function() { // temporary solution while code:brush is still used, later this section will be removed $('.source_section').find('code[class*="brush"]').each(function () { var _this = $(this); var formatClass = 'src-'; if (_this.hasClass('css')) { formatClass += 'css'; } else if (_this.hasClass('js')) { formatClass += 'js'; } else if (_this.hasClass('json')) { formatClass += 'json'; } else { formatClass += 'html'; } if (_this.hasClass('source_visible')) { formatClass += ' source_visible '; } if (!_this.parent().is('pre')) { _this.html().trim(); _this.wrap('
');
                    }

                    if (_this.hasClass('html')) {
                        var HTMLcode = _this.html();
                        _this.html(HTMLcode.replace(//g,">"));
                    }

                    _this.parent()
                        .attr('class', formatClass)
                        .attr('style', 'display:none');

                    _this[0].removeAttribute("class");
                });

                // wrapper for new syntax: 
                $('.source_section').find('code[class*="src-"]').each(function () {
                    var _this = $(this);

                    if (!_this.parent().is('pre')) {
                        _this.html().trim();
                        _this.wrap('
');
                    }

                    if (_this.hasClass('src-html')) {
                        var HTMLcode = _this.html();
                        _this.html(HTMLcode.replace(//g,">"));
                    }

                    _this.parent()
                        .attr('class', _this.attr('class'))
                        .attr('style', 'display:none');

                    _this[0].removeAttribute('class');
                });
            };

            //Code show toggle on each code block
            var prepareCodeBlocks = function() {
                new Css('/source/assets/js/lib/prism/prism.css', 'core');
                var selection = onlyStatic ? $('.source_section pre[class*="src-"].source_visible > code') : $('.source_section pre[class*="src-"] > code');
                selection.each(function () {
                    var _this = $(this);
                    var parent = _this.parent();
                    var langClass='';
                    if (!parent.hasClass('src-json')) {
                        if (parent.hasClass('src-css')) {
                            langClass = SourceCodeToggleCSS;
                        } else if (parent.hasClass('src-js')) {
                            langClass = SourceCodeToggleJS;
                        } else {
                            langClass = SourceCodeToggleHTML;
                        }
                        if (parent.hasClass('source_visible')) {
                            parent.wrap('
'); } else if (!parent.hasClass('src-json')) { parent.wrap('
'); _this.closest('.' + SourceCode).prepend('' + '' + RES_HIDE_CODE + '' + RES_SHOW_CODE + ' ' + RES_CODE + '' + ''); } Prism.highlightElement(_this[0]); } }); }; var fillCodeContainers = function() { //Auto copy HTML in code.html if it's now filled var selection = onlyStatic ? $('.source_section pre[class*="src-"].source_visible > code') : $('.source_section pre[class*="src-"] > code'); selection.each(function () { var _this = $(this); var HTMLcode; if (_this.html().trim().length === 0) { HTMLcode = _this.parent().nextAll('.'+ options.exampleSectionClass ).html(); _this.html(HTMLcode); if (_this.parent().hasClass('src-html')) { codeFormat(_this); } } else { HTMLcode = _this.html(); var spaces = HTMLcode.replace(/\t/,' ').match(/^\s{0,}/)[0].length; var HTMLarray = HTMLcode.trim().split("\n"); for (var i=0; i code').addClass('__visible'); $('pre.source_visible').removeAttr('style'); //Scroll to section var navHash = utils.parseNavHash(); utils.scrollToSection(navHash); }; var hideAllCode = function () { $('.' + SourceCode).removeClass(SourceCodeShow); $('.' + SourceCodeToggleAll).removeClass(SourceCodeToggleAllHide); //Scroll to section var navHash = utils.parseNavHash(); utils.scrollToSection(navHash); }; if ($('[class*="src-"]:not(.source_visible)')[0]) { innerNavigation.addMenuItem(RES_TOGGLER_SHOW_CODE, showAllCode, hideAllCode, 'source_source-code_action-item'); } showStaticCode(); }); } }); ================================================ FILE: assets/js/modules/couch.js ================================================ /* * * Couch module and utils * * jquery.cocuh docs http://daleharvey.github.io/jquery.couch.js-docs/symbols/ * jquery.couchlib http:///_utils/script/jquery.couch.js * * */ define([ 'jquery', 'sourceModules/module', 'sourceModules/utils', 'sourceLib/jquery.couch' ], function ($, module, utils, couch) { 'use strict'; function Couch() { this.options.modulesOptions.couch = $.extend({ server: 'http://127.0.0.1:5984' }, this.options.modulesOptions.couch); $.couch.urlPrefix = this.options.modulesOptions.couch.server; } Couch.prototype = module.createInstance(); Couch.prototype.constructor = Couch; //Preparing remote storage object, and create it if its not ready Couch.prototype.prepareRemote = function (dbname, doc, id) { var _this = this; var dfd = new $.Deferred(); var dbName = dbname; var storedData = doc; var specID = id; storedData['_id'] = specID; var db = $.couch.db(dbName); db.openDoc(specID, { success: function(data) { dfd.resolve(data); }, error: function(status) { if (status === 404) { console.log('creating new node'); $.when( _this.updateRemote(dbName, storedData) ).then( function(data) { dfd.resolve(data); } ); } } }); return dfd.promise(); }; /* fn(remote db name, actual data [, data to update] [, pass existing deffered] ) Actual data storing is needed for sending updated data to remote, prepared object for sending must have same _id and _rev as remote stored object. */ Couch.prototype.updateRemote = function (dbname, actualData, dataToUpdate, deffered) { var _this = this; var dbName = dbname; var dataToStore = actualData; var updateData = dataToUpdate; var db = $.couch.db(dbName); var dfd = typeof deffered !== 'object' ? new $.Deferred() : deffered; //If need to update if (typeof dataToUpdate === 'object') { dataToStore = $.extend(dataToStore, updateData); } db.saveDoc(dataToStore, { success: function(data) { //Server send response status var response = data; //If remote object succesfully updated if (response.ok) { //Return updated data dfd.resolve(dataToStore); } else { console.log("Failed to update remote object"); dfd.reject("Failed to update remote object"); } }, error: function(status) { console.log("Failed to update remote object, maybe DB is not created?"); //Create new db db.create({ success: function(data) { if (data === 412) { console.log("DB alreaty exist"); dfd.reject("DB alreaty exist"); } else if (data.ok) { console.log("New DB created, trying to fill it"); //Try again _this.updateRemote(dbName, dataToStore, updateData, dfd); } }, error: function(status) { console.log("Failed to create db"); dfd.reject("Failed to create db"); } }); } }); return dfd.promise(); }; return new Couch(); }); ================================================ FILE: assets/js/modules/css.js ================================================ 'use strict'; define(["source/load-options"], function(options) { function Css (url, cat) { this.url = url; this.cat = cat || 'plugin'; this.inject(); } Css.prototype.inject = function(){ var href = this.url; var link = document.createElement("link"); link.type = "text/css"; link.rel = "stylesheet"; link.dataset.source = this.cat; link.href = href; document.getElementsByTagName("head")[0].appendChild(link); }; return Css; }); ================================================ FILE: assets/js/modules/globalNav.js ================================================ /* * * @author Robert Haritonov (http://rhr.me) * * */ define([ "jquery", "sourceModules/module", "sourceModules/utils", "sourceModules/parseFileTree", "sourceModules/ntf", "sourceLib/lodash" ], function($, module, utils, parseFileTree, ntf, _) { "use strict"; /** * @Object defaults. It represents preseted options to initialize * navigation module. Can be overrided by options.moduleOptions.globalNav. */ var defaults = { "filterEnabled": true, "useHeaderUrlForNavigation": true, "showPreviews": false, "sortType": "sortByDate", "sortDirection":"forward", "delimeter": ",", "pageLimit": 999, "ignorePages": [], "thumbnailName": "thumbnail.png", "classes": { "catalog": "source_catalog", "catalogList": "source_catalog_list", "catalogListItem": "source_catalog_list_i", "catalogListAll": "source_catalog_all", "catalogLinkToAll": "source_a_bl", "catalogImageThumbler": "source_catalog_image-tumbler", "catalogListLink": "source_catalog_a", "catalogListImage": "source_catalog_img", "catalogListTitle": "source_catalog_title", "catalogListDate": "source_catalog_footer", "catalogFilter" : "source_catalog-filter", "sourceSubhead" : "source_subhead", "catalogText": "source_catalog_tx", "showPreview": "__show-preview", "updateButton": "source_catalog_update-button" }, "labels": { "noDataInCat": "No data in specified nav category", "linkToAllSpecs": "All specs", "author" : "Author", "noDataAttr" : "Data-nav attr not set", "loading": "Loading...", "hidePreview": "Hide thumbnails", "showPreview": "Show thumbnails", "updateButton": "Update navigation" }, "templates": {} }; /** * @constructor GlobalNav * * @function GlobalNav. Module constructor. * It implements module initialization. */ function GlobalNav() { var _this = this; this.options.modulesOptions.globalNav = $.extend(true, defaults, this.options.modulesOptions.globalNav, JSON.parse(localStorage.getItem("source_enabledFilter")) || {} ); this.initTemplates(); $(function() { _this.init(); }); } GlobalNav.prototype = module.createInstance(); GlobalNav.prototype.constructor = GlobalNav; /** * @returns Object templates. Contains basic navigation templates. * It uses lo-dash template function. */ GlobalNav.prototype.initTemplates = function() { this.templates = $.extend(true, { catalogList: _.template([ '
    ', '<%= labels.loading %>', '
' ].join("")), catalogHeader: _.template('

<%= catalogMeta.title %>

'), catalogMeta: _.template('
<%= catalogMeta.info %>
'), catalogLinkToAll: _.template([ '
  • ', '<%= labels.linkToAllSpecs %> <%= length %> ', '
  • ' ].join("")), navigationListItem: _.template([ '
  • ', '', '', '
    ', '
    ', '
  • ' ].join("")), catalogFilter: _.template('
    '), togglePreviewLink: _.template(''), updateButton: _.template(''), sortList: _.template([ '' ].join("")) }, this.options.modulesOptions.globalNav.templates); }; /** * @method init. This method implements initial module definition. */ GlobalNav.prototype.init = function () { var navOptions = this.options.modulesOptions.globalNav; this.catalog = $("." + navOptions.classes.catalog); this.renderNavigation(); if (this.options.modulesOptions.globalNav.filterEnabled) { this.initCatalogFilter(); } }; /** * @private * @method skipSpec. Filtering by specified catalogue * * @param {String|Array} navListCat catalogue type * @param {Object} obj - object to filter * * @returns {Boolean} true if spec shoud be skipped. */ var skipSpec = function(navListCat, obj) { obj = obj || {}; // obj["cat"] is an array; if cat has needed value var isSingleTag = !!obj["tag"] && typeof(navListCat) === "string"; var inArray = isSingleTag ? !!~obj["tag"].indexOf(navListCat) : !!obj["tag"] && _.reduce(navListCat, function(inArray, item) { return inArray && !!~obj['tag'].indexOf(item); }, true); // without-cat mode, showing all specs without cat field in info.json defined or var isWithoutCat = navListCat === "without-tag" && (!obj["tag"] || obj["tag"].length === 0); return !inArray && !isWithoutCat; }; /** * @private * @method isHidden. It helps to filter hidden specs. * * @param {Object} obj - spec to check. * * @returns {Boolean} true if spec is hidden. */ var isHidden = function(obj) { return !!obj["tag"] && !!~obj["tag"].indexOf("hidden"); }; /** * @method initCatalog - initialize catalog DomElement * * @param {Object} catalog - Catalog DomElement * @param {Object} catalogMeta - additional catalog information. * @param {Boolean} specifCatAndDirDefined - boolean flag, which defines if some cat and dir are defined */ GlobalNav.prototype.initCatalog = function(catalog, catalogMeta, specifCatAndDirDefined) { var config = this.options.modulesOptions.globalNav; var classes = config.classes; if (catalog.find("." + classes.catalogList).length === 0) { catalog.append(this.templates.catalogList(config)); } if (specifCatAndDirDefined || !catalogMeta) { return; } var isHeaderAdded = ( catalog.find("." + classes.catalogListTitle).length !== 0 || catalog.children("h2").length !== 0 ); var isInfoAdded = ( catalog.find("." + classes.catalogText).length !== 0 || catalog.children("p").length !== 0 ); if (catalogMeta && !isHeaderAdded) { catalog.prepend(this.templates.catalogHeader({"classes": classes, "catalogMeta": catalogMeta})); } if (catalogMeta && catalogMeta.info && $.trim(catalogMeta.info) !== "" && !isInfoAdded) { catalog .children("." + classes.catalogListTitle) .first() .after(this.templates.catalogMeta({"classes": classes, "catalogMeta": catalogMeta})); } }; /** * @method renderNavigation. Drawing navigation and page info in each catalog defined on page. * * @param {String} [sortType] - type of sorting * @param {String} [sortDirection] - ASC || DESC */ GlobalNav.prototype.renderNavigation = function (sortType, sortDirection) { var _this = this; var navOptions = this.options.modulesOptions.globalNav; var classes = navOptions.classes; var labels = navOptions.labels; sortType = sortType || navOptions.sortType; sortDirection = sortDirection || navOptions.sortDirection; this.catalog.each(function () { var catalog = $(this); var navListDir = _this.processDefinedTargetCatalogue(catalog.attr("data-nav")); var navListCat = catalog.attr("data-tag"); // Catalog has no data about category var targetCatalog = parseFileTree.getCurrentCatalogSpec(navListDir) || {}; _this.initCatalog(catalog, targetCatalog, !!navListDir && !!navListCat); if (navListDir && !navListDir.length) { // Display error catalog.find("." + classes.catalogList).html(labels.noDataAttr); return; } var targetData = parseFileTree.getSortedCatalogsArray(navListDir, _this.getSortCondition(sortType, sortDirection)); _this.renderNavigationList(catalog, targetData); }); // Scroll to hash after render var navHash = utils.parseNavHash(); utils.scrollToSection(navHash); }; /** * @method renderNavigationList. It draws navigation list into catalog. * * @param {Object} catalog - DomElement to fill * @param {Object} data - content */ GlobalNav.prototype.renderNavigationList = function(catalog, data) { var navOptions = this.options.modulesOptions.globalNav; var delimeter = navOptions.delimeter; var target = catalog.find("." + navOptions.classes.catalogList); var navListDir = catalog.attr("data-nav"); var navListCat = catalog.attr("data-tag"); navListCat = typeof(navListCat) === "string" ? navListCat.split(' ').join('') : navListCat; navListCat = navListCat && !!~navListCat.indexOf(delimeter) ? navListCat.split(delimeter) : navListCat; var catalogHeaderURL = catalog.find("." + navOptions.classes.catalogListTitle + '>a').attr('href'); var filter = function(spec) { var isInIgnoreList = !spec || !spec.title || !spec.url || !!~$.inArray(spec.title, navOptions.ignorePages); var isFiltered = !!navListDir && !!navListCat && skipSpec(navListCat, spec) || isHidden(spec); return isInIgnoreList || isFiltered ? false : true; }; if (!data || !data.length) { target.html(navOptions.labels.noDataInCat); return; } if(target && target.length === 1) { var url = catalogHeaderURL && catalogHeaderURL.length && navOptions.useHeaderUrlForNavigation ? catalogHeaderURL : navListDir; var specs = _.reduce(data, function(result, item) { filter(item["specFile"]) && result.push(item); return result; }, []); target.html(this.getNavigationItemsList(specs, url)); } }; /** * @methor getNavigationItemsList. It creates the list of navigation items. * * @param [Array] specifications - list of items to create nav items. * @param {String} catalogUrl - URL to catalog * @param [function] isValid - callback to check if spec is valid. * * @returns {Object} document fragment which contains list of navItems. */ GlobalNav.prototype.getNavigationItemsList = function(specifications, catalogUrl) { // temporary container to hold navigation items. var navigationItemsList = document.createDocumentFragment(); var self = this; var navOptions = this.options.modulesOptions.globalNav; var pageLimit = navOptions.pageLimit; var classes = navOptions.classes; var labels = navOptions.labels; var lengthLimit = pageLimit > specifications.length ? specifications.length : pageLimit; var specsToShow = specifications.slice(0, lengthLimit); _.map(specsToShow, function(item) { navigationItemsList.appendChild(self.renderNavTreeItem(item["specFile"]).get(0)); }); // Go to cat page link if (specifications.length > lengthLimit) { navigationItemsList.appendChild( $(this.templates.catalogLinkToAll({ classes: classes, labels: labels, url: catalogUrl, length: specifications.length })).get(0) ); } return navigationItemsList; }; /** * @method renderNavTreeItem. Returns single navigation tree item. It uses item template for it. * * @param {Object} itemData - data of single list item. * * @returns {Object} result - rendering result */ GlobalNav.prototype.renderNavTreeItem = function(itemData) { if (!itemData || !itemData.url || !itemData.title) return; var navConfig = this.options.modulesOptions.globalNav; var classes = navConfig.classes; var lastMod = itemData.lastmod || ''; var author = itemData.author ? navConfig.labels.author + ": " + itemData.author : ''; var metaInfo = ''; var addToMeta = function(data){ if (data === '') return; if (metaInfo === '') { metaInfo = data; } else { metaInfo += ' | ' + data; } }; addToMeta(lastMod); addToMeta(author); if (metaInfo === '') metaInfo = ' '; // fixing relative path due to server settings var itemDataUrl = itemData.url.charAt(0) === "/" ? itemData.url : "/" + itemData.url; var imageUrl = itemData.thumbnail; if (!this.renderNavTreeItem.template) { this.renderNavTreeItem.template = this.templates.navigationListItem(navConfig); } var result = $(this.renderNavTreeItem.template).clone(true); result.find("." + classes.catalogListLink.split(" ").join(".")).attr("href", itemDataUrl); if (imageUrl) { result.find("." + classes.catalogListLink) .prepend(''); } result.find("." + classes.catalogListTitle).html(itemData.title); result.find("." + classes.catalogListDate).html(metaInfo); return result; }; /** * @method initCatalogFilter - function that initializes filters. * Rendering is also calls from it. */ GlobalNav.prototype.initCatalogFilter = function() { var classes = this.options.modulesOptions.globalNav.classes; var $subhead = $("." + classes.sourceSubhead); var $catalog = $("." + classes.catalog); if (!$subhead.length || !$catalog.length) return; this.renderFilters($subhead); }; /** * @method renderFilters. It renders filters layout. * * @param {Object} filtersTarget - dom element which is going to be * container for rendering. */ GlobalNav.prototype.renderFilters = function(filtersTarget) { var classes = this.options.modulesOptions.globalNav.classes; if (!filtersTarget.find("." + classes.catalogFilter).length) { filtersTarget.prepend(this.templates.catalogFilter({"classes": classes})); } this.renderSortFilters(); this.renderPreviewsToggler(); this.updateButton(); }; /** * @method renderPreviewsToggler. It draws preview toggler. */ GlobalNav.prototype.renderPreviewsToggler = function() { var classes = this.options.modulesOptions.globalNav.classes; var labels = this.options.modulesOptions.globalNav.labels; var showPreviews = this.options.modulesOptions.globalNav.previews; var initPreviewValue = localStorage.getItem( "source_showPreviews") || showPreviews; var $filter = $("." + classes.catalogFilter); var catalog = this.catalog; if (initPreviewValue === "true") { // initPreviewValue is string, not boolean catalog.addClass(classes.showPreview); $filter.append(this.templates.togglePreviewLink({"classes": classes, "togglePreviewLabel": labels.hidePreview})); } else { $filter.append(this.templates.togglePreviewLink({"classes": classes, "togglePreviewLabel": labels.showPreview})); } $(document).on("click", "." + classes.catalogImageThumbler, function(e) { e.preventDefault(); var showPreviews = localStorage.getItem( "source_showPreviews"); var $this = $(this); var previewText; if (showPreviews === "true") { // string previewText = labels.showPreview; localStorage.setItem("source_showPreviews" , false); } else { previewText = labels.hidePreview; localStorage.setItem("source_showPreviews", true); } $this.text(previewText); catalog.toggleClass(classes.showPreview); }); }; /** * @method updateButton. It draws link that allows to trigger page update. */ GlobalNav.prototype.updateButton = function() { var classes = this.options.modulesOptions.globalNav.classes; var labels = this.options.modulesOptions.globalNav.labels; var $filter = $("." + classes.catalogFilter); $filter.append( ' | ', this.templates.updateButton({ classes: classes, labels: labels } )); $(document).on("click", "." + classes.updateButton, function(e) { e.preventDefault(); $.get('/api/updateFileTree') .done(function (data) { if (utils.isDevelopmentMode()) console.log('Navigation update done: ', data.message); location.reload(); }) .fail(function (err) { console.log('Error updating navigation: ', err); ntf.alert('Error updating navigation'); }); }); }; /** * @method renderSortFilters. It draws Sort Filters layout. */ GlobalNav.prototype.renderSortFilters = function() { var defaultSort = this.options.modulesOptions.globalNav.sortType; var $filterWrapper = $("." + this.options.modulesOptions.globalNav.classes.catalogFilter); var enabledFilter = JSON.parse(localStorage.getItem("source_enabledFilter")) || {"sortType":defaultSort,"sortDirection":"forward"}; var nav = this.templates.sortList(); var _this = this; $filterWrapper.append(nav); var $activeFilter = $("#" + enabledFilter.sortType); $activeFilter.parent().addClass("__active"); if (enabledFilter.sortDirection === "forward") { $activeFilter.parent().addClass("__forward"); } var updateView = function(el) { var $this = el; var sortType = $this.attr("id"); var sortDirection = "backward"; $(".source_sort-list_li").removeClass("__active"); $this.parent() .addClass("__active") .toggleClass("__forward"); if ( $this.parent().hasClass("__forward") ) { sortDirection = "forward"; } enabledFilter.sortType = sortType; enabledFilter.sortDirection = sortDirection; localStorage.setItem("source_enabledFilter", JSON.stringify(enabledFilter)); _this.renderNavigation(sortType, sortDirection); }; $(document).on("click", ".source_sort-list_a", function() { updateView($(this)); }); }; /** * @method getSortCondition. It defines current sorting property and order. * * @param {String} type - on of predefined sort types. * @param {String} direction - ASC or DESC sort odrer * * @returns [Function] sortingCallback - function to sort items. */ GlobalNav.prototype.getSortCondition = function(type, direction) { var multiplexer = direction === "forward" ? 1 : -1; if (type === "sortByAlph") { return function sortByAlph(a, b) { if (!a["specFile"].title || !b["specFile"].title) return 0; a = a["specFile"].title.replace(/(^\s+|\s+$)/g, ""); b = b["specFile"].title.replace(/(^\s+|\s+$)/g, ""); if (a === b) return 0; return multiplexer * ((a > b) ? 1 : -1); }; } // type === "sortByDate", this is default one return function sortByDate(a, b) { a = parseInt(a["specFile"].lastmodSec, 10); b = parseInt(b["specFile"].lastmodSec, 10); if(a === b) return 0; return multiplexer * ((a > b) ? -1 : 1); }; }; GlobalNav.prototype.processDefinedTargetCatalogue = function(targetCatalogue) { var endTarget = targetCatalogue; var relativeRegExt = new RegExp(/^.\//); // If relative with `./` if (relativeRegExt.test(endTarget)) { endTarget = endTarget.replace(relativeRegExt, utils.getPathToPage() + '/'); } return endTarget; }; return new GlobalNav(); }); ================================================ FILE: assets/js/modules/headerFooter.js ================================================ define(['source/load-options'], function(options) { 'use strict'; var $header = $(".source_header"); var $footer = $(".source_footer"); if(options.modulesEnabled.headerFooter) { var headerExists = $header.length > 0; var footerExists = $footer.length > 0; //Header and Footer injection var source_container = $('.source_container'); if (!headerExists) { source_container.prepend($('
    ')); } if (!footerExists) { source_container.append($('')); } if (source_container.length && !(headerExists && footerExists)) { //some empty page if (!source_container.contents().length) { source_container.append($('
    Welcome to Source!
    ')); } var insertHeader = function(data) { $('.source_header').replaceWith(data.responseText); }; var headerFile = 'header.inc.html'; var footerFile = 'footer.inc.html'; $.ajax({ url:"/assets/templates/"+headerFile, async:true, complete:function (data, status) { if (status === 'success') { insertHeader(data); } else { $.ajax({ url:"/source/assets/templates/"+headerFile, async:true, complete:function (data) { insertHeader(data); } }); } } }); var insertFooter = function(data) { $('.source_footer').replaceWith(data.responseText); }; $.ajax({ url:"/assets/templates/"+footerFile, async:true, complete:function (data, status) { if (status === 'success') { insertFooter(data); } else { $.ajax({ url:"/source/assets/templates/"+footerFile, async:true, complete:function (data) { insertFooter(data); } }); } } }); } } //click on header - go up $header.on({ mouseover: function(e){ if(e.target === this){ $('.source_header').css('cursor', 'pointer'); }else { $('.source_header').css('cursor', 'inherit'); } }, click: function(e){ if(e.target === this){ window.scrollTo(0, 0); } }, mouseout : function(){ $('.source_header').css('cursor', 'inherit'); } }); }); ================================================ FILE: assets/js/modules/htmlAPISync.js ================================================ 'use strict'; define([ "jquery", "sourceModules/module", "sourceModules/utils", "sourceModules/innerNavigation", "sourceModules/ntf", "sourceModules/sectionsParser" ], function($, module, utils, innerNavigation, ntf) { function HtmlAPiSync() { this.specID = utils.getPathToPage().substring(1); innerNavigation.addMenuItemSimple('HTML API', [ { name: 'Sync', callback: this.syncHTML }, { name: 'Delete', callback: this.deleteHTML } ], this); } HtmlAPiSync.prototype = module.createInstance(); HtmlAPiSync.prototype.constructor = HtmlAPiSync; HtmlAPiSync.prototype.syncHTML = function(){ var parser = new SourceGetSections(); var specObj = {}; specObj[this.specID + '/specFile'] = parser.getSpecFull(); // Marking hand synced content specObj[this.specID + '/specFile/forcedSave'] = true; var dataToSend = { unflatten: true, data: specObj }; $.ajax({ url: '/api/specs/html', method: 'POST', data: JSON.stringify(dataToSend), contentType: "application/json", dataType: 'json', success: function(){ ntf.alert('HTML Sync done'); } }); }; HtmlAPiSync.prototype.deleteHTML = function(){ var dataToSend = { id: this.specID }; $.ajax({ url: '/api/specs/html', method: 'DELETE', data: JSON.stringify(dataToSend), contentType: "application/json", dataType: 'json', success: function(){ ntf.alert('HTML Delete done'); } }); }; return new HtmlAPiSync(); }); ================================================ FILE: assets/js/modules/inlineOptions.js ================================================ define(function() { 'use strict'; // empty array for cases where `properties` is not defined in-page return {}; }); ================================================ FILE: assets/js/modules/innerNavigation.js ================================================ 'use strict'; define([ "jquery", "sourceModules/module", "sourceModules/utils", "sourceModules/sections", "sourceModules/headerFooter", "text!sourceTemplates/nav.inc.html", "text!sourceTemplates/navActionItem.inc.html", "text!sourceTemplates/navActionTumbler.inc.html"], function ($, module, utils, sections, headerFooter, navTemplate, menuItemTemplate, menuItemTumblerTemplate) { function InnerNavigation() { var _this = this; this.options.modulesOptions.innerNavigation = $.extend({}, { NAV_UL_CLASS: 'source_main_nav_ul', NAV_UL_SECONDLEVEL_CLASS: 'source_main_nav_ul2', NAV_LI_CLASS: 'source_main_nav_li', NAV_LI_SECONDLEVEL_CLASS: 'source_main_nav_li2', NAV_LINK_CLASS: 'source_main_nav_a', MENU__I_CLASS: 'source_main_nav_i', MENU_SCROLL_MOD_CLASS: '__menuScroll', MAIN_NAV_AC: 'source_main_nav_ac', MAIN_NAV_AC_TX: 'source_main_nav_ac_tx', INJECT_AFTER: '.source_main h1', RESERVED_HEIGHT: 250 // (185 + 15 + 50) px }, this.options.modulesOptions.innerNavigation); this.moduleOptions = this.options.modulesOptions.innerNavigation; this.menuItemTemplate = $(menuItemTemplate); this.menuItemTumblerTemplate = $(menuItemTumblerTemplate); this.container = $(navTemplate); var $injectAfter = $(this.moduleOptions.INJECT_AFTER).first(); // Checking if inject after element exists, and isn't used in source example sections if ( $injectAfter.length !== 0 && $injectAfter.parents('.'+this.options.exampleSectionClass).length === 0 && $injectAfter.parents('.'+this.options.exampleCleanClass).length === 0 ) { $injectAfter.after(this.container); } else { $('.'+this.options.mainClass).prepend(this.container); } $(function () { _this.injectNavigation(); _this.calcMenuHeight(); _this.bindEvents(); }); } InnerNavigation.prototype = module.createInstance(); InnerNavigation.prototype.constructor = InnerNavigation; //className is optional InnerNavigation.prototype.addMenuItem = function (text, onCallback, offCallback, className) { var newItem = this.menuItemTumblerTemplate.clone(); if (typeof className === 'string' && className !== '') { newItem.addClass(className); } newItem.find(".source_slider_frame").click(function (e) { e.preventDefault(); var isEnabled = $(this).hasClass("source_slider_frame__on"); isEnabled = !Boolean(isEnabled); $(this).toggleClass("source_slider_frame__on"); if (isEnabled) { onCallback(); } else { offCallback(); } }); newItem.find('.' + this.moduleOptions.MAIN_NAV_AC_TX).text(text); $('.' + this.moduleOptions.MAIN_NAV_AC).append(newItem); // recalculate height after adding new action to menu $(window).trigger('resize'); }; //className is optional InnerNavigation.prototype.addMenuItemSimple = function (text, actionsArr, ctx, className) { var newItem = this.menuItemTemplate.clone(); if (typeof className === 'string' && className !== '') { newItem.addClass(className); } actionsArr.forEach(function(action, index){ var $link = $(''); $link.text(action.name); $link.on('click', function(e){ e.preventDefault(); if (ctx) { action.callback.apply(ctx); } else { action.callback(); } }); newItem.find('.source_main_nav_ac-list').append($link); if (index !== actionsArr.length - 1){ newItem.find('.source_main_nav_ac-list').append(' / '); } }); newItem.find('.' + this.moduleOptions.MAIN_NAV_AC_TX).text(text); $('.' + this.moduleOptions.MAIN_NAV_AC).prepend(newItem); // recalculate height after adding new action to menu $(window).trigger('resize'); }; InnerNavigation.prototype.toggleMenuItem = function (id) { $('.'+id+' .source_slider_frame').toggleClass("source_slider_frame__on"); }; InnerNavigation.prototype.bindEvents = function () { var _this = this; $(window).on('resize',function () { _this.addMenuScrolling(); }).trigger('resize'); }; InnerNavigation.prototype.calcMenuHeight = function () { var h = 0; this.container.find('.' + this.moduleOptions.MENU__I_CLASS).each(function () { h += $(this).height(); }); return h; }; InnerNavigation.prototype.injectNavigation = function () { var appendString = ''; for (var i = 0; i < sections.getQuantity() ; i++) { var section = sections.getSections()[i]; var sectionID = i + 1; var href = (section.id); appendString += [ '
  • ', '', section.num + '. ' + section.caption, '' ].join(''); if ( section.subHeaderElements !== undefined ) { appendString += '
      '; for (var j = 0; j < section.subHeaderElements.length; j++) { var seqNum = j+1; var subSection = section.subHeaderElements[j]; var subSectionID = sectionID + '.' + seqNum; var subHref = subSection.attr('id') || subSectionID; appendString += [ '
    • ', '', section.num + '.' + seqNum + ' ' + subSection.text(), '', '
    • ' ].join(''); } appendString += '
    '; } appendString += '
  • '; } $('.' + this.moduleOptions.NAV_UL_CLASS).append(appendString); }; //TODO: refactor menu scrolling add InnerNavigation.prototype.addMenuScrolling = function () { if (this.calcMenuHeight() + this.moduleOptions.RESERVED_HEIGHT > $(window).height()) { this.container.addClass(this.moduleOptions.MENU_SCROLL_MOD_CLASS); } else { this.container.removeClass(this.moduleOptions.MENU_SCROLL_MOD_CLASS); } }; return new InnerNavigation(); }); ================================================ FILE: assets/js/modules/loadEvents.js ================================================ /** * * Register any plugins and emit their finish events * */ define(["sourceModules/module", "sourceModules/utils"], function(module, utils) { 'use strict'; function LoadEvents() {} LoadEvents.prototype = module.createInstance(); LoadEvents.prototype.constructor = LoadEvents; LoadEvents.prototype.init = function( callback ) { callback = callback || function () {}; var evt; var complete = false; var debug = module.options.modulesEnabled.loadEvents && module.options.modulesOptions.loadEvents && module.options.modulesOptions.loadEvents.debug; if ( (!module.options.modulesEnabled.loadEvents) || (window.CustomEvent === undefined && document.createEvent === undefined) ) { if (debug && utils.isDevelopmentMode()) { console.log('LoadEvents Module disabled.'); } callback(); return; } else { if (debug && utils.isDevelopmentMode()) { console.log('LoadEvents Module enabled.'); } } function checkPluginsDefinition() { return (window.source !== undefined) && (window.source.loadEvents !== undefined) && (Object.keys(window.source.loadEvents).length); } function phantomHook(event) { console.log('PhantomJS hook: %s', event); if (typeof window.callPhantom === 'function') { window.callPhantom({message: event}); } } function generateSuccessEvent(eventName) { if (debug && utils.isDevelopmentMode()) { console.log('event happens ' + eventName ); } if (typeof window.CustomEvent === 'function') { evt = new CustomEvent(eventName); } else { evt = document.createEvent('CustomEvent'); evt.initCustomEvent(eventName, false, false, {}); } document.dispatchEvent(evt); if (!complete) { complete = true; callback(); } } (function checkPluginsState() { // If there's no any render plugin, just skip checking section if ( !checkPluginsDefinition() ) { generateSuccessEvent('allPluginsFinish'); phantomHook('allPluginsFinish'); return; } var pluginsSection = window.source.loadEvents; var defaultPluginTimeout = 700; function checkPlugins() { var now = new Date().getTime(); var pluginFinish = 0; var onFinishEvent = function() { if (debug && utils.isDevelopmentMode()) { console.log(plugin + ' drop finishEvent' ); } pluginsSection[plugin].finish = true; pluginsSection[plugin].timeExpected = now-1; }; var onUpdateEvent = function() { var tmpExpected = now + parseInt(pluginsSection[plugin].timeout || defaultPluginTimeout, 10); if (tmpExpected > pluginsSection[plugin].timeExpected) { pluginsSection[plugin].timeExpected = tmpExpected; if (debug && utils.isDevelopmentMode()) { console.log(plugin + ' drop updateEvent and increase timeout to ' + pluginsSection[plugin].timeExpected ); } } }; /*jshint forin: false */ for (var plugin in pluginsSection) { if (!pluginsSection.hasOwnProperty(plugin)) { continue; } if (debug && utils.isDevelopmentMode()) { console.log(plugin + ' checking... '); } if (pluginsSection[plugin].finish) { pluginsSection[plugin].timeExpected = now-1; if (debug && utils.isDevelopmentMode()) { console.log(plugin + ' marked as finished'); } } if ( !Array.isArray( pluginsSection[plugin]) ) { // first time if (pluginsSection[plugin].timeExpected === undefined) { pluginsSection[plugin].timeExpected = now + parseInt(pluginsSection[plugin].timeout || defaultPluginTimeout, 10); if (debug && utils.isDevelopmentMode()) { console.log(plugin + ' was registred and set timeout ' + pluginsSection[plugin].timeExpected); } // When plugin finish his work, it send "finishEvent", which differs for each plugin pluginsSection[plugin].finishEvent && document.addEventListener(pluginsSection[plugin].finishEvent, onFinishEvent); // When plugin need to update work's timeout, it generate "updateEvent" pluginsSection[plugin].updateEvent && document.addEventListener(pluginsSection[plugin].updateEvent, onUpdateEvent); // Plugin already was initialized } else { // If timeout come, increase counter if ( pluginsSection[plugin].timeExpected < now ) { if (!pluginsSection[plugin].finish) { generateSuccessEvent(plugin + ' timeout, ' + new Date().getTime()); pluginsSection[plugin].finish = true; } pluginFinish++; } } } else { var subPluginFinish = 0; pluginFinish++; if ( (pluginsSection[plugin].length) && (!pluginsSection[plugin].finish) ) { for (var i = 0; i < pluginsSection[plugin].length; i++ ) { var subPlugin = pluginsSection[plugin][i]; if ( (pluginsSection[subPlugin] !== undefined) && (pluginsSection[subPlugin].timeExpected < now) ) { subPluginFinish++; } } if ( pluginsSection[plugin].length === subPluginFinish) { pluginsSection[plugin].finish = true; generateSuccessEvent(plugin + ' GroupFinish'); } } } } // if there's more than 0 plugins and all of them get timeout, continue, // otherwise make recursive call if ( Object.keys(pluginsSection).length !== pluginFinish ) { setTimeout(function() { checkPlugins(); }, 100); return; } setTimeout(function () { generateSuccessEvent('allPluginsFinish'); phantomHook('allPluginsFinish'); }, defaultPluginTimeout); return; } checkPlugins(); })(); }; return new LoadEvents(); }); ================================================ FILE: assets/js/modules/module.js ================================================ define(["source/load-options"], function (options) { 'use strict'; function Module() { this.options = {}; this.setOptions(this.loadOptions()); } Module.prototype.loadOptions = function () { return options; }; Module.prototype.setOptions = function (opts) { this.options = opts; return this.options; }; Module.prototype.getOptions = function () { return this.options; }; Module.prototype.getClass = function () { return this.constructor.name; }; Module.prototype.createInstance = function () { return new this.constructor(); }; return new Module(); }); ================================================ FILE: assets/js/modules/moduleLoader.js ================================================ define([ 'sourceModules/module' ], function (module) { 'use strict'; function ModuleLoader() { this.loadModules('modules'); this.loadModules('plugins'); this.loadModules('npmPlugins'); } /* наследуем от Module */ ModuleLoader.prototype = module.createInstance(); ModuleLoader.prototype.constructor = ModuleLoader; ModuleLoader.prototype.loadModules = function(type){ var path; var typeEnabled; var optionsBase = this.options; // Override options with exceptions var isNav = $('meta[name=source-page-role]').attr('content') === 'navigation'; if (isNav) { optionsBase = this.options.navPageModulesBuild; } if (type === 'modules') { typeEnabled = 'modulesEnabled'; path = 'sourceModules/'; } else if (type === 'plugins') { typeEnabled = 'pluginsEnabled'; path = 'plugins/'; } else if (type === 'npmPlugins') { typeEnabled = 'npmPluginsEnabled'; path = ''; } else { console.log('Invalid loadModules argument'); return; } if (typeof optionsBase[typeEnabled] !== 'undefined'){ for(var item in optionsBase[typeEnabled]) { if (optionsBase[typeEnabled].hasOwnProperty(item)) { var targetObj = optionsBase[typeEnabled][item]; if (targetObj){ require([path + item]); } } } } }; return new ModuleLoader(); }); ================================================ FILE: assets/js/modules/navHighlight.js ================================================ /* * * Navigation scroll highlighter * * @author Ivan Metelev (http://welovehtml.ru) * * */ "use strict"; define([ 'sourceModules/module' ], function (module) { function NavHighlight() { var defaults = { updateHash: true }; this.options.modulesOptions.navHighlight = $.extend(true, defaults, this.options.modulesOptions.navHighlight); this.conf = this.options.modulesOptions.navHighlight; this.init(); } NavHighlight.prototype = module.createInstance(); NavHighlight.prototype.constructor = NavHighlight; NavHighlight.prototype.init = function(){ var _this = this; // basic utils var utils = (function() { return { offsetTop: function(elem) { var box = elem.getBoundingClientRect(); var clientTop = document.body.clientTop || 0; var top = box.top + window.pageYOffset - clientTop; return Math.round(top); }, hasClass: function(elem, cls) { if (elem !== null) { return elem.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')); } else { return false; } }, addClass: function(elem, cls) { if (!this.hasClass(elem,cls)) { elem.className += " "+cls; elem.className.replace(/ +/g,' '); } }, removeClass: function(elem, cls) { if (this.hasClass(elem,cls)) { var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)'); elem.className=elem.className.replace(reg,''); } }, closest: function(elem, cls) { //console.log(elem, cls); if (elem.tagName === 'html') return false; var elemParent = elem.parentNode; if (!utils.hasClass(elem, cls)) { return utils.closest(elemParent, cls); } return elem; } }; })(); var sourceHeaders = []; var navHeaders; var currentHeader = -1; var filename = ''; var extension = ''; var hashThreshold = 100; var fullFilename = window.location.href.split("#")[0].split('/').pop().split('.'); //update extension and filename only if there is one in URL if(fullFilename.length !== 1) { extension = /\w+/.exec(fullFilename[1]); filename = fullFilename[0]; } // watch headers position var watchSectionTop = function () { var headersLength = sourceHeaders.length; var minDistance = Number.MAX_VALUE; var closestHeader = -1; var fileNameInUrl = filename === '' ? '' : filename + '.' + extension; if ((document.body.scrollTop || document.documentElement.scrollTop) < hashThreshold) { if (!!window.location.hash) { currentHeader = -1; if (!!(window.history && history.pushState)) { window.history.replaceState({anchor: 0}, document.title, window.location.pathname); } } return; } // catch section which is closed for top window border for (var i=0; i < headersLength; i++) { if ((sourceHeaders[i].tagName === 'H3') && (!utils.hasClass(utils.closest(sourceHeaders[i], 'source_section'), 'source_section__open')) ) { continue; } var currentDist = Math.abs( utils.offsetTop(sourceHeaders[i]) - 60 - window.pageYOffset ); //60 = Header heights if (currentDist < minDistance) { closestHeader = i; minDistance = currentDist; } } if (closestHeader !== currentHeader) { utils.removeClass( document.querySelector('.source_main_nav_li.__active'), '__active'); utils.removeClass( document.querySelector('.source_main_nav_a.__active'), '__active'); utils.addClass(navHeaders[closestHeader], '__active'); var parent = utils.closest(navHeaders[closestHeader], 'source_main_nav_li'); var hashFromLink = navHeaders[closestHeader].getAttribute('href'); if (!!parent && parent) { utils.addClass(parent, '__active'); } if (_this.conf.updateHash) { // TODO: pause hash change when scrolling - append it only on stand-still // Modern browsers uses history API for correct back-button-browser functionality if (!!(window.history && history.pushState)) { window.history.replaceState({anchor: closestHeader+1}, document.title, fileNameInUrl + hashFromLink); } else { // ie9 fallback window.location.hash = hashFromLink; } } currentHeader = closestHeader; } }; // watch navmenu render var checkNavInterval; var h2Nodes; var bodyNode = document.querySelector('body'); var checkOnNav = function checkOnNav() { if ((document.querySelector('.source_section') !== null) && (document.querySelector('.source_main_nav_a') !== null)) { clearInterval(checkNavInterval); navHeaders = document.querySelectorAll('.source_main_nav_a'); h2Nodes = document.querySelectorAll('.source_section'); var getH3Nodes = function(childerArray){ var h3Nodes = []; childerArray.forEach(function (item) { if (item.tagName === 'H3') h3Nodes.push(item); }); return h3Nodes; }; for (var h2 = 0; h2 < h2Nodes.length; h2++) { var childerArray = [].slice.call(h2Nodes[h2].children); var h3Nodes = getH3Nodes(childerArray); sourceHeaders.push( h2Nodes[h2].querySelector('h2') ); for (var h3 = 0; h3 < h3Nodes.length; h3++) { sourceHeaders.push( h3Nodes[h3] ); } } if (utils.hasClass(bodyNode, 'source__scrolled-down')) { watchSectionTop(); } window.onscroll = function() { watchSectionTop(); }; return; } }; checkNavInterval = setInterval(checkOnNav, 1000); }; return new NavHighlight(); }); ================================================ FILE: assets/js/modules/ntf.js ================================================ 'use strict'; define([ 'jquery', 'sourceModules/module' ], function($, module) { function Notification() { if ($('.source_ntf').length === 0) { $('.source_container').append('
    '); this.$el = $('.source_ntf'); } } Notification.prototype = module.createInstance(); Notification.prototype.constructor = Notification; Notification.prototype.alert = function(msg){ var _this = this; clearTimeout(this.clearNotif); this.$el.text(msg).addClass('__active'); this.clearNotif = setTimeout(function(){ _this.$el.text(''); _this.$el.removeClass('__active'); }, 1000); }; return new Notification(); }); ================================================ FILE: assets/js/modules/parseFileTree.js ================================================ /* * * Module for parsing Source file tree data * * @author Robert Haritonov (http://rhr.me) * @author Ivan Metelev * * */ 'use strict'; define([ 'jquery', 'sourceModules/module', 'text!/api/specs/raw', 'text!/api/specs?cats=true' ], function ($, module, data, allSpecs) { function ParseFileTree() { this.json = JSON.parse(data); } ParseFileTree.prototype = module.createInstance(); ParseFileTree.prototype.constructor = ParseFileTree; //getSpecificCat = a || [a,b] - Get only listed category, categories //getCatInfo = bool - Turn on cat info parsing /** * Abstract pages tree parser * * @params {String} getSpecificCat - Category name * @params {Boolean} getCatInfo - Get category info or not * @returns {Object} Return data object with specs * * @deprecated since version 0.4, use REST API instead */ ParseFileTree.prototype.parsePages = function (getSpecificCat, getCatInfo) { var _this = this; var json = _this.json; var fileTree = {}; var totalTree = {}; var searchCat = function(currentCatObj, currentCat, toCheckCat) { var returnObject = function(returnedTreeObj, excludeRootDocument) { var isSingle = false; if (getSpecificCat.indexOf('specFile') === -1) { for (var innerCat in returnedTreeObj) { if ( _this.checkCatInfo(returnedTreeObj[innerCat], innerCat, getCatInfo) ) { if (innerCat === 'specFile' && (!excludeRootDocument)) { fileTree[innerCat] = {}; fileTree[innerCat]['specFile'] = returnedTreeObj[innerCat]; } else { fileTree[innerCat] = returnedTreeObj[innerCat]; } } } } else { fileTree['specFile'] = {}; fileTree['specFile']['specFile'] = returnedTreeObj; isSingle = true; } return isSingle; }; /*jshint forin: false */ for (var currentSubCat in currentCatObj) { if (!currentCatObj.hasOwnProperty(currentSubCat)) { continue; } var targetSubCatObj = currentCatObj[currentSubCat]; if (typeof targetSubCatObj === 'object') { //Need to collect only spec pages objects if ( _this.checkCatInfo(targetSubCatObj, currentSubCat, getCatInfo) && _this.checkCat(currentCat, getSpecificCat, toCheckCat) ) { //Checking if object is already there if (typeof fileTree[currentSubCat] !== 'object') { fileTree[currentCat + '/' + currentSubCat] = targetSubCatObj; } } else { //Going deeper if (getSpecificCat) { getSpecificCat = getSpecificCat.replace(/index.html/i, 'specFile'); } var returnedTreeObj; // complex/paths/handles/here if ( (getSpecificCat !== undefined) && (getSpecificCat.indexOf('/') !== -1) ) { var getSpecificCatArr = getSpecificCat.split('/'); var success = true; if (getSpecificCatArr[ getSpecificCatArr.length-1 ] === '') { getSpecificCatArr.pop(); } // absolute path if (getSpecificCat.indexOf('/') === 0) { returnedTreeObj = _this.json; for (var i = 1; i < getSpecificCatArr.length; i++) { if (!returnedTreeObj) return; returnedTreeObj = returnedTreeObj[ getSpecificCatArr[i] ]; } if (returnObject(returnedTreeObj, true)) return; } else { //relative path var currentCheckCat = currentSubCat; returnedTreeObj = currentCatObj; for (var j = 0; j < getSpecificCatArr.length; j++) { if (_this.checkCat(currentCheckCat, getSpecificCatArr[j])) { currentCheckCat = getSpecificCatArr[j + 1]; } else { success = false; break; } returnedTreeObj = returnedTreeObj[ getSpecificCatArr[j] ]; } if (success) { if (returnObject(returnedTreeObj)) return; } } } else if (_this.checkCat(currentCat, getSpecificCat, toCheckCat)) { //Turn off cat checking in this process, to get all inner folders //Except other cats in specific cat search mode if (typeof getCatInfo !== 'undefined') { // Checking navigation page info if (typeof targetSubCatObj['specFile'] !== 'object' ) { searchCat(targetSubCatObj, currentSubCat, true); } } else { searchCat(targetSubCatObj, currentSubCat, true); } } else { searchCat(targetSubCatObj, currentSubCat); } } } } }; //Parse first level folders for (var currentCat in json) { if (json.hasOwnProperty(currentCat)) { var currentCatObj = json[currentCat]; //Go inside first level folders searchCat(currentCatObj, currentCat); totalTree = $.extend(totalTree, fileTree); } } if (Object.getOwnPropertyNames(fileTree).length > 0) { return totalTree; } }; ParseFileTree.prototype.checkCatInfo = function (targetSubCatObj, currentSubCat, getCatInfo) { if (targetSubCatObj) { //If cat info needed if (getCatInfo) { return typeof targetSubCatObj['specFile'] === 'object' || currentSubCat === 'specFile'; } else { return typeof targetSubCatObj['specFile'] === 'object'; } } }; ParseFileTree.prototype.checkCat = function (currentCat, getSpecificCat, toCheckCat) { var checkCat = function() { //Multiple check cat support if (typeof getSpecificCat === 'string') { return getSpecificCat === currentCat; } else if (typeof getSpecificCat === 'object') { //If array var something = false; for (var i = 0; i < getSpecificCat.length; i++) { if (getSpecificCat[i] === currentCat) { something = true; break; } } return something; } else { return false; } }; //Turn off cat checking in specific process if (toCheckCat === true) { return true; } else { //Check if cat checking is set if (typeof getSpecificCat !== 'undefined') { return checkCat(); } else { return true; } } }; /** * Get specific cat pages without category info * * @params {String} getSpecificCat - Category name * @returns {Object} Return data object with specs * * @deprecated since version 0.4, use REST API instead */ ParseFileTree.prototype.getCatPages = function (getSpecificCat) { //Get cat pages return this.parsePages(getSpecificCat); }; /** * Get specific cat pages with category * * @params {String} getSpecificCat - Category name * @returns {Object} Return data object with specs * * @deprecated since version 0.4, use REST API instead */ ParseFileTree.prototype.getCatAll = function (getSpecificCat) { //Get cat pages with cat info return this.parsePages(getSpecificCat, true); }; var getCurrCatalogSpec = function(navListDir, targetCatalog) { var catObj; if (!!targetCatalog[navListDir + '/specFile']) { catObj = targetCatalog[navListDir + '/specFile']; } else if (!!targetCatalog[ 'specFile' ]) { catObj = targetCatalog['specFile']['specFile'] ? targetCatalog['specFile']['specFile'] : targetCatalog['specFile']; } return catObj; }; ParseFileTree.prototype.getCurrentCatalogSpec = function(catalogName) { if (!catalogName) return; var specsHash = this.parsePages(catalogName, true); if (!specsHash) return; return getCurrCatalogSpec(catalogName, specsHash); }; ParseFileTree.prototype.getSortedCatalogsArray = function(catalogName, sortingCallback) { if (catalogName === undefined) return; var specsHash; var newStructure = true; if (catalogName.length) { newStructure = false; specsHash = this.parsePages(catalogName, true); } else { specsHash = JSON.parse(allSpecs); } if (!specsHash) return; var result = []; for (var key in specsHash) { if (specsHash.hasOwnProperty(key)) { var value = newStructure ? specsHash[key] : specsHash[key]['specFile']; if (!value) continue; result.push({ name: value.url, specFile: value }); } } return result.sort(sortingCallback); }; return new ParseFileTree(); }); ================================================ FILE: assets/js/modules/scrollToHash.js ================================================ //In separate module, to trace script ready state define([ "jquery", "sourceModules/utils", "sourceModules/loadEvents", "sourceModules/sectionFolding" ], function($, utils, loadEvents) { 'use strict'; $(function(){ var navHash = utils.parseNavHash(); var $sections = $('.source_section'); var $navigation = $('.source_nav'); var $mainContainer = $('.source_main'); var $body = $('body'); // Show hidden sections and navigation function showSections() { $sections.addClass('__loaded'); $navigation.addClass('__loaded'); $mainContainer.removeClass('__loading'); $body.removeClass('__loading'); } $mainContainer.addClass('__loading'); $body.addClass('__loading'); loadEvents.init(function() { showSections(); // FF scroll bug, related native to scroll to hash conflicts if ($('html').hasClass('mozilla')) { var triesCount = 0; var t = setInterval(function() { var scrollTopCord = utils.scrollToSection(navHash); if (window.pageYOffset === scrollTopCord || triesCount < 4) { clearInterval(t); } else { triesCount++; } }, 300); } else { utils.scrollToSection(navHash); } }); }); }); ================================================ FILE: assets/js/modules/search.js ================================================ /* * * @author Ilya Mikhailov * * */ define([ 'jquery', 'sourceModules/module', 'sourceLib/autocomplete', 'sourceLib/modalbox', 'sourceModules/parseFileTree', 'sourceModules/globalNav', 'sourceModules/headerFooter' ], function ($, module, autocomplete, ModalBox, parseFileTree, globalNav) { 'use strict'; var Search = function() { this.data = []; this.options.modulesOptions.search = $.extend (true, { classes: { searchResult: "__search-res", headerFocus: "source_header__focus" }, labels: { searchResults: "Search results:", showAllButtonText: "Show all search results" }, suggestionsLimit: 4, activateOnLoad: true }, this.options.modulesOptions.search); this.init(); return this; }; Search.prototype.constructor = Search; Search.prototype = module.createInstance(); Search.prototype.init = function() { var _this = this; this.activated = false; this.targetField = $('#livesearch'); this.header = $('.source_header'); this.prepareAutoCompleteData(); this.initSearchField(); setTimeout(function() { _this.targetField.attr('data-initialized', 'true'); }, 1); }; Search.prototype.prepareAutoCompleteData = function() { this.data = this.data || []; if (this.data.length) return; var AutocomleteDataItem = function (value, data) { this.value = value; this.data = data; }; var sort = JSON.parse(localStorage.getItem("source_enabledFilter")) || {"sortDirection": "forward", "sortType": "sortByAlph"}; var pagesData = parseFileTree.getSortedCatalogsArray("", globalNav.getSortCondition(sort.sortType, sort.sortDirection)); for (var page in pagesData) { if (pagesData.hasOwnProperty(page)) { var targetPage = pagesData[page]['specFile']; // Skip hidden specs if (targetPage.tag && targetPage.tag.indexOf('hidden') > -1) continue; var keywords = targetPage.keywords; var keywordsPageName = pagesData[page] && pagesData[page]['name'] ? pagesData[page]['name'] : ""; //get cat name var prepareKeywords = ''; var autocompleteValue = targetPage.title; if (keywords && keywords !== '') { prepareKeywords += ', ' + keywords; } autocompleteValue += ' (' + keywordsPageName + prepareKeywords + ')'; this.data[this.data.length] = new AutocomleteDataItem(autocompleteValue, targetPage.url); } } }; Search.prototype.wrapSearchResults = function(results) { var modulesOptions = this.options.modulesOptions; var classes = [ modulesOptions.globalNav.classes.catalogList, modulesOptions.search.classes.searchResult, modulesOptions.globalNav.classes.catalog ]; if (modulesOptions.globalNav.showPreviews) { classes.push("__show-preview"); } var list = $('
      '); $.map(results, function(item) { var specItem = parseFileTree.getCatAll(item.data).specFile; specItem.title = item.value; list.append(globalNav.renderNavTreeItem(specItem)); }); return list; }; Search.prototype.activateAutocomplete = function() { var _this = this; var searchOptions = this.options.modulesOptions.search; this.targetField.autocomplete({ "lookup": _this.data, "autoSelectFirst": true, "suggestionsLimit": searchOptions.suggestionsLimit, "labels": { "showAllButtonText": searchOptions.labels.showAllButtonText }, "showAll": function (suggestions) { (new ModalBox({ "appendTo": ".source_main" }, { "title": searchOptions.labels.searchResults, "body": _this.wrapSearchResults(suggestions) })).show(); } }); this.activated = true; }; function getActivationHandler(ctx) { return function() { ctx.header.addClass(ctx.activeClass); if (!ctx.isActive) { ctx.callback(); } }; } Search.prototype.initSearchField = function() { var isNavigation = $('meta[name=source-page-role]').attr('content') === 'navigation'; var searchOptions = this.options.modulesOptions.search; var _this = this; if (searchOptions.activateOnLoad) { this.activateAutocomplete(); } var autofocusOnNavEnabled = isNavigation && searchOptions.autofocusOnNavigationPage; var autofocusOnSpecEnabled = !isNavigation && searchOptions.autofocusOnSpecPage; if (autofocusOnNavEnabled || autofocusOnSpecEnabled) { setTimeout(function() { //First focus fix _this.targetField.focus(); }, 1); } var eventContext = { header: _this.header, activeClass: searchOptions.classes.headerFocus, isActive: _this.activated, callback: function() { return _this.activateAutocomplete(); } }; //Unblur header on focus this.targetField.on({ 'focus': getActivationHandler(eventContext), 'blur':function () { _this.header.removeClass(searchOptions.classes.headerFocus); } }); this.targetField.one('keyup', getActivationHandler(eventContext)); }; return new Search(); }); ================================================ FILE: assets/js/modules/sectionFolding.js ================================================ /* * * Fold sections to optimize page load * * @author Robert Haritonov (http://rhr.me) * * */ define([ "jquery", "source/load-options", "sourceModules/utils", "sourceModules/browser", "sourceModules/sections", "sourceModules/innerNavigation" ], function($, options, utils, browser, sections, innerNavigation) { 'use strict'; $(function(){ //TODO: move to utils // Bulletproof localStorage check var storage; var fail; var uid; try { uid = new Date(); (storage = window.localStorage).setItem(uid, uid); fail = storage.getItem(uid) !== uid.toString(); storage.removeItem(uid); fail && (storage = false); } catch (e) { } //TODO: /move to utils var SECTION_CLASS = options.SECTION_CLASS; var L_SECTION_CLASS = $('.'+SECTION_CLASS); var OPEN_SECTION_CLASS = 'source_section__open'; var sectionsOnPage = L_SECTION_CLASS; var specName = utils.getSpecName(); //Определяем название спеки var clientConfig = {}; var RES_HIDE_SECTIONS = 'Hide all sections'; if (storage) { //Check if localstorage has required data if (typeof localStorage.getItem('LsClientConfig') === 'string') { //LocalStorage can store only strings //Using JSON for passing array through localStorage clientConfig = $.parseJSON(localStorage.getItem('LsClientConfig')); } else { try { localStorage.setItem('LsClientConfig', JSON.stringify(clientConfig)); } catch (e) { } } } //Preparing config if (typeof clientConfig[specName] !== 'object') { clientConfig[specName] = {}; clientConfig[specName].closedSections = {}; } else if (typeof clientConfig[specName].closedSections !== 'object') { clientConfig[specName].closedSections = {}; } /* Config must look like: clientConfig = { covers: { closedSections: { section1: true, section2: true } } } */ var closedSections = clientConfig[specName].closedSections; var updateConfig = function () { try { localStorage.setItem('LsClientConfig', JSON.stringify(clientConfig)); } catch (e) { } }; var openSpoiler = function ($target, config) { if ($target.is('h3')) { $target = $target.closest('.source_section'); } $target.addClass(OPEN_SECTION_CLASS); var sectionID = $target.attr('id'); var isRendered = false; closedSections["section" + sectionID] = false; if (config) { //Remember options in localStorage updateConfig(); } // TODO: remove mustache check from core if (options.pluginsOptions && options.pluginsOptions.mustache) { // Need to use absolute path to get same scope with requires from inline scripts require(['/plugins/mustache/js/mustache.js'], function(templater){ if (typeof templater.PostponedTemplates !== 'undefined') { if ($target.attr('data-rendered') === 'true') { isRendered = true; } if (!isRendered) { for (var arr in templater.PostponedTemplates[sectionID]) { if (templater.PostponedTemplates[sectionID].hasOwnProperty(arr)) { var values = templater.PostponedTemplates[sectionID][arr]; templater.insertTemplate(values[0], values[1], values[2]); } } $target.attr('data-rendered', 'true'); } } }); } }; var closeSpoiler = function (t, config) { t.removeClass(OPEN_SECTION_CLASS); var tID = t.attr('id'); closedSections["section" + tID] = true; if (config) { updateConfig(); } }; var navHash = utils.parseNavHash(); var sectionsCount = sectionsOnPage.length; for (var i = 0; i < sectionsCount; i++) { var $this = $(sectionsOnPage[i]); var tID = $this.attr('id'); //Check from local storage config if (closedSections["section" + tID]) { $this.attr('data-def-stat', 'closed'); } //Open all unclosed by confing spoilers and scroll to hash targeted section //For ie < 8 all sections closed by default if ($this.attr('data-def-stat') !== 'closed' && !(browser.msie && parseInt(browser.version, 10) < 8)) { openSpoiler($this); } } if (navHash !== '') { openSpoiler($(navHash)); } //To open sections on from inner navigation var openOnNavigation = function() { var navHash = utils.parseNavHash(); openSpoiler($(navHash)); //Close other closed by default sections for (var i = 0; i < sectionsOnPage.length; i++) { var t = $(sectionsOnPage[i]); var tID = t.attr('id'); if (t.attr('data-def-stat') === 'closed' && navHash !== '#' + tID) { closeSpoiler(t); } } if (navHash !== '') { utils.scrollToSection(navHash); } }; //If supports history API if (window.history && history.pushState && !browser.msie) { window.addEventListener('popstate', function (event) { openOnNavigation(); }); } else { $('.source_main_nav_a').on({ click: function(e) { e.preventDefault(); //Imitate hash change on click var href = $(this).attr('href'); href = href.split('#'); href = href[href.length - 1]; window.location.hash = href; openOnNavigation(); } }); } var toggleSpoiler = function (t) { if (t.hasClass(OPEN_SECTION_CLASS)) { closeSpoiler(t, true); } else { openSpoiler(t, true); } }; for(var j = 0; j < sections.getQuantity(); j++) { sections.getSections()[j].headerElement .addClass("source_section_h") .append(''); } $('.source_section_h_expand').on({ click:function () { $this = $(this).closest('.source_section'); toggleSpoiler($this); } }); innerNavigation.addMenuItem(RES_HIDE_SECTIONS, function(){ for (var i = 0; i < sectionsOnPage.length; i++) { closeSpoiler($(sectionsOnPage[i]), true); } }, function(){ for (var i = 0; i < sectionsOnPage.length; i++) { openSpoiler($(sectionsOnPage[i]), true); } }); }); }); ================================================ FILE: assets/js/modules/sections.js ================================================ /* * * Sections modules * * @author Alexey Ostrovsky * * */ define([ "jquery", "sourceModules/module" ], function ($, module) { 'use strict'; function Sections() { var _this = this; this.sections = []; $(function(){ _this.scanDOM(); }); } Sections.prototype = module.createInstance(); Sections.prototype.constructor = Sections; Sections.prototype.scanDOM = function () { var _this = this; $("." + this.getOptions().SECTION_CLASS).each(function (index, elem) { var section = $(elem); var headerElement = section.find("h2:first"); var subHeaders = section.children("h3"); var subHeaderElements = []; for (var i=0; i < subHeaders.length; i++) { var targetSubHeader = subHeaders[i]; subHeaderElements[subHeaderElements.length] = $(targetSubHeader); } var sect = { num: index + 1, id: _this.setSectionId(section, index + 1), caption: headerElement.text(), sectionElement: section, headerElement: headerElement, subHeaderElements: subHeaderElements }; _this.addSection(sect); }); return this.getSections(); }; Sections.prototype.getSections = function () { return this.sections; }; Sections.prototype.addSection = function (sec) { return this.sections.push(sec); }; Sections.prototype.getQuantity = function () { return this.getSections().length; }; // Adding unique ID to section, if it's present Sections.prototype.setSectionId = function (section, id) { var _id = id; var custom; if (section.attr('id') !== undefined) { custom = section.attr('id'); } else { section.attr('id', _id); } section.children('h3').each(function(index) { if ($(this).attr('id') === undefined) { $(this).attr('id', _id + '.' + (index+1)); } }); return custom || _id; }; return new Sections(); }); ================================================ FILE: assets/js/modules/sectionsParser.js ================================================ /* * * Spec HTML parser for API * * @author Dmitry Lapynow * * */ // TODO: wrap as requirejs module, and combine with phantomRunner.js function SourceGetSections() { // Defining strict inside func, because PhantomJS stops evaluating this script if it's on top 'use strict'; var config = { // include params from opt@argument first code: 'source_example', sectionHeader: '.source_section > h2:first-child', h3: 'H3', h4: 'H4', timeout: 3000 }; var _h2 = 0, _h3 = 0, _h4 = 0; var elem; var prevFlag; var root = false; function getSections() { var specData = []; var sections = [].slice.call(document.querySelectorAll(config.sectionHeader)); if (sections[0]) { for (var i = 0, l = sections.length; i < l; i++) { var section = sections[i]; elem = section; specData.push(parse(section)); } return specData; } else { return undefined; } } function parse(section) { _h2++; _h3 = 0; _h4 = 0; return new Spec(section); } function Spec(section) { if (section.next){ elem = (elem) ? getNextSibling(elem) : null; } this.id = returnId(section); this.visualID = visualID(); this.header = section.header || getHeader(elem); this.nested = []; this.html = []; while (elem) { if (root) { elem = getPreviousSibling(elem); root = false; break; } var tag = elem.tagName, cls = elem.className, flag = checkElem(tag, cls) ; if (flag === 'H2') { this.header = getHeader(elem); prevFlag = flag; } else if (flag === 'CODE') { this.html.push(getHTML(elem)); } else if (flag === 'H3') { if (prevFlag === 'H4') { root = true; } if (prevFlag === 'H3' || prevFlag === 'H4') { prevFlag = null; elem = getPreviousSibling(elem); break; } _h3++; _h4 = 0; prevFlag = flag; this.nested.push(new Spec({header: getHeader(elem), next: true, headerElem: elem})); } else if (flag === 'H4') { if (prevFlag === flag) { prevFlag = null; elem = getPreviousSibling(elem); break; } _h4++; prevFlag = flag; this.nested.push(new Spec({header: getHeader(elem), next: true, headerElem: elem})); } if (elem) { elem = getNextSibling(elem); } } } // HELPERS function getNextSibling(elem) { // JSdom doesn't support nextElementSibling var nextSibling = elem.nextSibling; // Checking if next sibling is an element, not whitespace while(nextSibling && nextSibling.nodeType !== 1) { nextSibling = nextSibling.nextSibling; } return nextSibling; } function getPreviousSibling(elem) { // JSdom doesn't support previousElementSibling var previousSibling = elem.previousSibling; // Checking if prev sibling is an element, not whitespace while(previousSibling && previousSibling.nodeType !== 1) { previousSibling = previousSibling.previousSibling; } return previousSibling; } function returnId(section) { var hasClass = function(el, className) { if (el.classList) { return el.classList.contains(className); } else { return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); } }; var checkCustomID = function (){ var parent = section.parentNode; if (parent && hasClass(parent, 'source_section') && parent.id) return parent.id; if (section.id) return section.id; if (section.headerElem && section.headerElem.id) return section.headerElem.id; return undefined; }; if (checkCustomID()) { return checkCustomID(); } else { return visualID(); } } function visualID() { var idArr = [_h2]; if (_h3 > 0) idArr.push(_h3); if (_h4 > 0) idArr.push(_h4); return idArr.join('.'); } function getHeader(elem) { return elem.textContent || 'API: cannot get header.'; } function getHTML(elem) { return elem.innerHTML; } function checkElem(tag, cls) { return isH2(tag, cls) || isH3(tag, cls) || isH4(tag, cls) || isCode(tag, cls); } function isH2(tag) { return (tag === 'H2') ? 'H2' : false; } function isH3(tag) { return (tag === 'H3')? 'H3' : false; } function isH4(tag) { return (tag === 'H4')? 'H4': false; } function isCode(tag, cls) { return ( (tag === 'SECTION' || tag === 'DIV') && cls.match(new RegExp('\\b'+ config.code + '\\b')) ) ? 'CODE' : false; } /* Start parser */ this.specHTMLContents = getSections(); } /** * Filter list of HTML nodes * * @param {Array} elementsArr - nodelist to filter * @param {Function} [customElFilter] - Additional filter for element * @param {Boolean} [skipAttrFilters] - Set to true, if expect not filtered list of resources * * @returns {Array} Returns array with HTML of Style containers OR undefined */ SourceGetSections.prototype.filterResourceElements = function(elementsArr, customElFilter, skipAttrFilters){ 'use strict'; var filteredArr = []; var defaultFilter = function(){ return true; }; var _customElFilter = typeof customElFilter === 'function' ? customElFilter : defaultFilter; // Checking some exceptions var checkDataSet = function(el){ if (skipAttrFilters) { return true; } else { return !( el.getAttribute('data-nonclarify') || el.getAttribute('data-requiremodule') || el.getAttribute('data-source') === 'core' || el.getAttribute('data-source') === 'plugin' ); } }; if (elementsArr && elementsArr.length > 0) { var i = 0; var el; while(i < elementsArr.length) { el = elementsArr[i]; if (_customElFilter(el) && checkDataSet(el)) { // If not need to filter out, then push to output filteredArr.push(el.outerHTML); } ++i; } return filteredArr.length > 0 ? filteredArr : undefined; } else { return undefined; } }; /** * Parse CSS links from Spec * * @param {Object} scope to search - document.head or document.body * @param {Boolean} [skipAttrFilters] - Set to true, if expect not filtered list of resources * * @returns {Array} Returns array with HTML of CSS links OR undefined */ SourceGetSections.prototype.getCssLinksHTML = function(scope, skipAttrFilters) { 'use strict'; var links = scope.getElementsByTagName('link'); var customFilter = function(el){ return el.rel === 'stylesheet' || el.type === 'text/css'; }; return this.filterResourceElements(links, customFilter, skipAttrFilters); }; /** * Parse Scripts from Spec * * @param {Object} scope to search - document.head or document.body * @param {Boolean} [skipAttrFilters] - Set to true, if expect not filtered list of resources * * @returns {Array} Returns array with HTML of Scripts OR undefined */ SourceGetSections.prototype.getScriptsHTML = function(scope, skipAttrFilters) { 'use strict'; var scripts = scope.getElementsByTagName('script'); return this.filterResourceElements(scripts, null, skipAttrFilters); }; /** * Parse Style containers from Spec * * @param {Object} scope to search - document.head or document.body * @param {Boolean} [skipAttrFilters] - Set to true, if expect not filtered list of resources * * @returns {Array} Returns array with HTML of Style containers OR undefined */ SourceGetSections.prototype.getStyleContainersHTML = function(scope, skipAttrFilters){ 'use strict'; var styles = scope.getElementsByTagName('style'); return this.filterResourceElements(styles, null, skipAttrFilters); }; /** * Flatten HTML contents * * @returns {Object} Updates global 'specHTMLContentsFlatObj' and return flat specHTMLContents */ SourceGetSections.prototype.flattenHTMLContents = function(){ // TODO: Move to CommonJS module, and reuse API/parseData.js 'use strict'; var flatList = {}; var parseContents = function(contents){ if (contents) { for (var i=0; contents.length > i ; i++) { var current = contents[i]; flatList[current.id] = current; if (current.nested.length > 0) { parseContents(current.nested); } } } }; parseContents(this.specHTMLContents); this.specHTMLContentsFlatObj = flatList; return flatList; }; /** * Get specific sections of Spec * * @param {Array} sections - Array of sections to return * * @returns {Object} Return array sections HTML OR undefined */ SourceGetSections.prototype.getContentsBySection = function(sections){ // TODO: Move to CommonJS module, and reuse API/parseData.js 'use strict'; var _this = this; if (Array.isArray(sections) && sections.length > 0) { // Check if flattened object is already prepared if (!_this.specHTMLContentsFlatObj) { _this.flattenHTMLContents(); } var pickedSections = []; sections.forEach(function(id){ var objectToAdd = _this.specHTMLContentsFlatObj[id]; if (objectToAdd) pickedSections.push(objectToAdd); }); return pickedSections.length === 0 ? undefined : pickedSections; } else { return undefined; } }; /** * Getter for contents of the Spec * * @returns {Object} Returns object with spec HTML contents OR undefined */ SourceGetSections.prototype.getContents = function(){ 'use strict'; return this.specHTMLContents; }; /** * Get full spec object * * @param {Array} [sections] - Array of sections to return * * @returns {Object} Returns object with spec HTML contents OR undefined */ SourceGetSections.prototype.getSpecFull = function(sections){ 'use strict'; var output = {}; output.headResources = this.getSpecResources() || {}; output.bodyResources = this.getSpecResources('body') || {}; if (sections) { output.contents = this.getContentsBySection(sections); } else { output.contents = this.specHTMLContents; } return (output.contents || output.headResources || output.bodyResources) ? output : undefined; }; /** * Getter for assets resources of the Spec * * @param {String} [scope] - "head" or "body", "head" is used by default * * @returns {Object} Returns object with HTML lists containing Spec assets resources OR undefined */ SourceGetSections.prototype.getSpecResources = function(scope){ 'use strict'; var _scope = scope === 'body' ? document.body : document.head; var output = {}; output.cssLinks = this.getCssLinksHTML(_scope); output.scripts = this.getScriptsHTML(_scope); output.cssStyles = this.getStyleContainersHTML(_scope); return (output.cssLinks || output.scripts || output.cssStyles) ? output : undefined; }; ================================================ FILE: assets/js/modules/specDecorations.js ================================================ define([ "jquery", "source/load-options", "sourceModules/browser" ], function($, options, browser) { 'use strict'; var SECTION_CLASS = options.SECTION_CLASS; var L_SECTION_CLASS = $('.'+SECTION_CLASS); var SCROLLED_DOWN_MOD_CLASS = 'source__scrolled-down'; //h3 decoration L_SECTION_CLASS.find('>h3').wrapInner(''); //IE layout fix if (browser.msie && parseInt(browser.version, 10) < 8) { //After demo section clear $('
      ').insertAfter('.source_section > .source_example'); } //Fading header on scroll down $(window).scroll(function () { if ($(window).scrollTop() > 50) { $('body').addClass(SCROLLED_DOWN_MOD_CLASS); } else { $('body').removeClass(SCROLLED_DOWN_MOD_CLASS); } }).trigger('scroll'); }); ================================================ FILE: assets/js/modules/trimSpaces.js ================================================ /* * * Media-queries helper * * @author Evgeniy Khoroshilov * * */ define([ "source/load-options", "sourceModules/codeSource" ], function(options) { 'use strict'; var EXAMPLE_CLASS = options.exampleSectionClass; var L_EXAMPLE_CLASS = $('.'+EXAMPLE_CLASS); //trim spaces between html tags in example sections $.fn.trimSpaces = function () { $(this).each(function () { var elem = $(this); var regex = new RegExp("\xa0"); if (elem.children().length) { elem.contents() .filter(function () { return this.nodeType === 3 && !/\S/.test(this.nodeValue); }) .filter(function () { return !regex.test(this.nodeValue); }).remove().end() .filter(function () { return regex.test(this.nodeValue); }).replaceWith(' '); elem.children().trimSpaces(); } }); return this; }; L_EXAMPLE_CLASS.trimSpaces(); }); ================================================ FILE: assets/js/modules/utils.js ================================================ /* * * Utils core for all modules * * */ define([ 'jquery', 'sourceModules/module' ], function ($, module) { 'use strict'; function Utils() {} /* наследуем от Module */ Utils.prototype = module.createInstance(); Utils.prototype.constructor = Utils; Utils.prototype.parseNavHash = function () { var hash = window.location.hash.replace('#/','#'); // Cleaning "!" from the end of hash, left for legacy link support if (hash.substring(hash.length-1) === "!") { hash = hash.substring(0, hash.length-1); } return hash; }; Utils.prototype.getUrlParameter = function (name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"); var results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); }; //sectionID = '#id' Utils.prototype.scrollToSection = function (sectionID) { // Modifying ID, for custom selection because of ids named "1.1", "2.2" etc var _sectionID = sectionID.replace('#',''); if (_sectionID === '') return; var newPosition = $(document.getElementById(_sectionID)).offset(); if (!newPosition) return; var newPositionPadding = $(".source_header").outerHeight() + 10; // Header height + padding var scrollTopPosition = newPosition.top - newPositionPadding; window.scrollTo(0, scrollTopPosition); return scrollTopPosition; }; Utils.prototype.getSpecName = function() { var specName; var pageUrl = window.location.pathname; var pageUrlSplit = pageUrl.split("/"); //Разбивает на массив specName = pageUrlSplit[pageUrlSplit.length - 2]; //Берет последнюю часть урла return specName; }; Utils.prototype.getPathToPage = function(specUrlFromFileTree) { var pathToSpec = (function(){ var path; //specUrl is used from parseFileTree, and contains only path, without file like "/docs/spec" if (typeof specUrlFromFileTree === 'undefined') { path = window.location.pathname; path = path.split("/"); //Breaking url to array path.splice(path.length - 1, 1); //Clear page.html from array path = path.join("/"); } else { path = specUrlFromFileTree; } return path; }()); return pathToSpec; }; Utils.prototype.hasClasses = function(element, selectorsArr) { for (var i in selectorsArr) { if (element.hasClass(selectorsArr[i])) return true; } return false; }; Utils.prototype.getCookie = function(name) { var matches = document.cookie.match(new RegExp("(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)")); return matches ? decodeURIComponent(matches[1]) : undefined; }; Utils.prototype.isDevelopmentMode = function() { return this.getCookie('source-mode') === 'development'; }; Utils.prototype.isArray = function(arr) { return Object.prototype.toString.call(arr) === '[object Array]'; }; Utils.prototype.unifySpecPath = function(url) { if (url.slice(-10) === "index.html") url = url.slice(0, -10); if (url.slice(-9) === "index.src") url = url.slice(0, -9); if (url.charAt(0) !== "/") url = "/" + url; if (url.charAt(url.length - 1) === "/") url = url.slice(0, -1); return url; }; Utils.prototype.toggleBlock = function(elToClick, elToShow) { var $elToClick = '.' + elToClick; $(document).on('click', $elToClick, function() { var $elToShow = $('.' + elToShow); $(this).toggleClass('__open'); $elToShow.toggle(); }); }; return new Utils(); }); ================================================ FILE: assets/js/require-config.js ================================================ // Joined to require.bundle.js requirejs.config({ paths: { // /source/assets requests are routed to /assets source: '/source/assets/js', sourceModules: '/source/assets/js/modules', sourceLib: '/source/assets/js/lib', sourceTemplates: '/source/assets/templates', // Require.js plugins text: '/source/assets/js/lib/text', // Relative to user root js: '/assets/js', plugins: '/plugins', node_modules: '/node_modules' } }); ================================================ FILE: assets/templates/clarifyPanel.inc.html ================================================

      Select sections

      Select template

      Options

        <% if (showApiTargetOption) { %>
      • <% } %> <% if (htmlParser) { %>
      • <% } %>
      Return to Spec
      ================================================ FILE: assets/templates/footer.inc.html ================================================ ================================================ FILE: assets/templates/header.inc.html ================================================
      ================================================ FILE: assets/templates/nav.inc.html ================================================ ================================================ FILE: assets/templates/navActionItem.inc.html ================================================
      Option
      ================================================ FILE: assets/templates/navActionTumbler.inc.html ================================================
      Option
      ON OFF
      ================================================ FILE: assets/test/index.html ================================================  Unit tests
      ================================================ FILE: assets/test/jasmine/MIT.LICENSE ================================================ Copyright (c) 2008-2011 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: assets/test/jasmine/jasmine-html.js ================================================ jasmine.HtmlReporterHelpers = {}; jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { var el = document.createElement(type); for (var i = 2; i < arguments.length; i++) { var child = arguments[i]; if (typeof child === 'string') { el.appendChild(document.createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (var attr in attrs) { if (attr == "className") { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; }; jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { var results = child.results(); var status = results.passed() ? 'passed' : 'failed'; if (results.skipped) { status = 'skipped'; } return status; }; jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { var parentDiv = this.dom.summary; var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; var parent = child[parentSuite]; if (parent) { if (typeof this.views.suites[parent.id] == 'undefined') { this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); } parentDiv = this.views.suites[parent.id].element; } parentDiv.appendChild(childElement); }; jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { for(var fn in jasmine.HtmlReporterHelpers) { ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; } }; jasmine.HtmlReporter = function(_doc) { var self = this; var doc = _doc || window.document; var reporterView; var dom = {}; // Jasmine Reporter Public Interface self.logRunningSpecs = false; self.reportRunnerStarting = function(runner) { var specs = runner.specs() || []; if (specs.length == 0) { return; } createReporterDom(runner.env.versionString()); doc.body.appendChild(dom.reporter); setExceptionHandling(); reporterView = new jasmine.HtmlReporter.ReporterView(dom); reporterView.addSpecs(specs, self.specFilter); }; self.reportRunnerResults = function(runner) { reporterView && reporterView.complete(); }; self.reportSuiteResults = function(suite) { reporterView.suiteComplete(suite); }; self.reportSpecStarting = function(spec) { if (self.logRunningSpecs) { self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); } }; self.reportSpecResults = function(spec) { reporterView.specComplete(spec); }; self.log = function() { var console = jasmine.getGlobal().console; if (console && console.log) { if (console.log.apply) { console.log.apply(console, arguments); } else { console.log(arguments); // ie fix: console.log.apply doesn't exist on ie } } }; self.specFilter = function(spec) { if (!focusedSpecName()) { return true; } return spec.getFullName().indexOf(focusedSpecName()) === 0; }; return self; function focusedSpecName() { var specName; (function memoizeFocusedSpec() { if (specName) { return; } var paramMap = []; var params = jasmine.HtmlReporter.parameters(doc); for (var i = 0; i < params.length; i++) { var p = params[i].split('='); paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); } specName = paramMap.spec; })(); return specName; } function createReporterDom(version) { dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, dom.banner = self.createDom('div', { className: 'banner' }, self.createDom('span', { className: 'title' }, "Jasmine "), self.createDom('span', { className: 'version' }, version)), dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), dom.alert = self.createDom('div', {className: 'alert'}, self.createDom('span', { className: 'exceptions' }, self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'), self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))), dom.results = self.createDom('div', {className: 'results'}, dom.summary = self.createDom('div', { className: 'summary' }), dom.details = self.createDom('div', { id: 'details' })) ); } function noTryCatch() { return window.location.search.match(/catch=false/); } function searchWithCatch() { var params = jasmine.HtmlReporter.parameters(window.document); var removed = false; var i = 0; while (!removed && i < params.length) { if (params[i].match(/catch=/)) { params.splice(i, 1); removed = true; } i++; } if (jasmine.CATCH_EXCEPTIONS) { params.push("catch=false"); } return params.join("&"); } function setExceptionHandling() { var chxCatch = document.getElementById('no_try_catch'); if (noTryCatch()) { chxCatch.setAttribute('checked', true); jasmine.CATCH_EXCEPTIONS = false; } chxCatch.onclick = function() { window.location.search = searchWithCatch(); }; } }; jasmine.HtmlReporter.parameters = function(doc) { var paramStr = doc.location.search.substring(1); var params = []; if (paramStr.length > 0) { params = paramStr.split('&'); } return params; } jasmine.HtmlReporter.sectionLink = function(sectionName) { var link = '?'; var params = []; if (sectionName) { params.push('spec=' + encodeURIComponent(sectionName)); } if (!jasmine.CATCH_EXCEPTIONS) { params.push("catch=false"); } if (params.length > 0) { link += params.join("&"); } return link; }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); jasmine.HtmlReporter.ReporterView = function(dom) { this.startedAt = new Date(); this.runningSpecCount = 0; this.completeSpecCount = 0; this.passedCount = 0; this.failedCount = 0; this.skippedCount = 0; this.createResultsMenu = function() { this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), ' | ', this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); this.summaryMenuItem.onclick = function() { dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); }; this.detailsMenuItem.onclick = function() { showDetails(); }; }; this.addSpecs = function(specs, specFilter) { this.totalSpecCount = specs.length; this.views = { specs: {}, suites: {} }; for (var i = 0; i < specs.length; i++) { var spec = specs[i]; this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); if (specFilter(spec)) { this.runningSpecCount++; } } }; this.specComplete = function(spec) { this.completeSpecCount++; if (isUndefined(this.views.specs[spec.id])) { this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); } var specView = this.views.specs[spec.id]; switch (specView.status()) { case 'passed': this.passedCount++; break; case 'failed': this.failedCount++; break; case 'skipped': this.skippedCount++; break; } specView.refresh(); this.refresh(); }; this.suiteComplete = function(suite) { var suiteView = this.views.suites[suite.id]; if (isUndefined(suiteView)) { return; } suiteView.refresh(); }; this.refresh = function() { if (isUndefined(this.resultsMenu)) { this.createResultsMenu(); } // currently running UI if (isUndefined(this.runningAlert)) { this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" }); dom.alert.appendChild(this.runningAlert); } this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); // skipped specs UI if (isUndefined(this.skippedAlert)) { this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" }); } this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; if (this.skippedCount === 1 && isDefined(dom.alert)) { dom.alert.appendChild(this.skippedAlert); } // passing specs UI if (isUndefined(this.passedAlert)) { this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" }); } this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); // failing specs UI if (isUndefined(this.failedAlert)) { this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); } this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); if (this.failedCount === 1 && isDefined(dom.alert)) { dom.alert.appendChild(this.failedAlert); dom.alert.appendChild(this.resultsMenu); } // summary info this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; }; this.complete = function() { dom.alert.removeChild(this.runningAlert); this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; if (this.failedCount === 0) { dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); } else { showDetails(); } dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); }; return this; function showDetails() { if (dom.reporter.className.search(/showDetails/) === -1) { dom.reporter.className += " showDetails"; } } function isUndefined(obj) { return typeof obj === 'undefined'; } function isDefined(obj) { return !isUndefined(obj); } function specPluralizedFor(count) { var str = count + " spec"; if (count > 1) { str += "s" } return str; } }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); jasmine.HtmlReporter.SpecView = function(spec, dom, views) { this.spec = spec; this.dom = dom; this.views = views; this.symbol = this.createDom('li', { className: 'pending' }); this.dom.symbolSummary.appendChild(this.symbol); this.summary = this.createDom('div', { className: 'specSummary' }, this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()), title: this.spec.getFullName() }, this.spec.description) ); this.detail = this.createDom('div', { className: 'specDetail' }, this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.spec.getFullName()), title: this.spec.getFullName() }, this.spec.getFullName()) ); }; jasmine.HtmlReporter.SpecView.prototype.status = function() { return this.getSpecStatus(this.spec); }; jasmine.HtmlReporter.SpecView.prototype.refresh = function() { this.symbol.className = this.status(); switch (this.status()) { case 'skipped': break; case 'passed': this.appendSummaryToSuiteDiv(); break; case 'failed': this.appendSummaryToSuiteDiv(); this.appendFailureDetail(); break; } }; jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { this.summary.className += ' ' + this.status(); this.appendToSummary(this.spec, this.summary); }; jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { this.detail.className += ' ' + this.status(); var resultItems = this.spec.results().getItems(); var messagesDiv = this.createDom('div', { className: 'messages' }); for (var i = 0; i < resultItems.length; i++) { var result = resultItems[i]; if (result.type == 'log') { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); } else if (result.type == 'expect' && result.passed && !result.passed()) { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); if (result.trace.stack) { messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); } } } if (messagesDiv.childNodes.length > 0) { this.detail.appendChild(messagesDiv); this.dom.details.appendChild(this.detail); } }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { this.suite = suite; this.dom = dom; this.views = views; this.element = this.createDom('div', { className: 'suite' }, this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description) ); this.appendToSummary(this.suite, this.element); }; jasmine.HtmlReporter.SuiteView.prototype.status = function() { return this.getSpecStatus(this.suite); }; jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { this.element.className += " " + this.status(); }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); /* @deprecated Use jasmine.HtmlReporter instead */ jasmine.TrivialReporter = function(doc) { this.document = doc || document; this.suiteDivs = {}; this.logRunningSpecs = false; }; jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { var el = document.createElement(type); for (var i = 2; i < arguments.length; i++) { var child = arguments[i]; if (typeof child === 'string') { el.appendChild(document.createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (var attr in attrs) { if (attr == "className") { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; }; jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { var showPassed, showSkipped; this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, this.createDom('div', { className: 'banner' }, this.createDom('div', { className: 'logo' }, this.createDom('span', { className: 'title' }, "Jasmine"), this.createDom('span', { className: 'version' }, runner.env.versionString())), this.createDom('div', { className: 'options' }, "Show ", showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") ) ), this.runnerDiv = this.createDom('div', { className: 'runner running' }, this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), this.runnerMessageSpan = this.createDom('span', {}, "Running..."), this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) ); this.document.body.appendChild(this.outerDiv); var suites = runner.suites(); for (var i = 0; i < suites.length; i++) { var suite = suites[i]; var suiteDiv = this.createDom('div', { className: 'suite' }, this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); this.suiteDivs[suite.id] = suiteDiv; var parentDiv = this.outerDiv; if (suite.parentSuite) { parentDiv = this.suiteDivs[suite.parentSuite.id]; } parentDiv.appendChild(suiteDiv); } this.startedAt = new Date(); var self = this; showPassed.onclick = function(evt) { if (showPassed.checked) { self.outerDiv.className += ' show-passed'; } else { self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); } }; showSkipped.onclick = function(evt) { if (showSkipped.checked) { self.outerDiv.className += ' show-skipped'; } else { self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); } }; }; jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { var results = runner.results(); var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; this.runnerDiv.setAttribute("class", className); //do it twice for IE this.runnerDiv.setAttribute("className", className); var specs = runner.specs(); var specCount = 0; for (var i = 0; i < specs.length; i++) { if (this.specFilter(specs[i])) { specCount++; } } var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); }; jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { var results = suite.results(); var status = results.passed() ? 'passed' : 'failed'; if (results.totalCount === 0) { // todo: change this to check results.skipped status = 'skipped'; } this.suiteDivs[suite.id].className += " " + status; }; jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { if (this.logRunningSpecs) { this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); } }; jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { var results = spec.results(); var status = results.passed() ? 'passed' : 'failed'; if (results.skipped) { status = 'skipped'; } var specDiv = this.createDom('div', { className: 'spec ' + status }, this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(spec.getFullName()), title: spec.getFullName() }, spec.description)); var resultItems = results.getItems(); var messagesDiv = this.createDom('div', { className: 'messages' }); for (var i = 0; i < resultItems.length; i++) { var result = resultItems[i]; if (result.type == 'log') { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); } else if (result.type == 'expect' && result.passed && !result.passed()) { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); if (result.trace.stack) { messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); } } } if (messagesDiv.childNodes.length > 0) { specDiv.appendChild(messagesDiv); } this.suiteDivs[spec.suite.id].appendChild(specDiv); }; jasmine.TrivialReporter.prototype.log = function() { var console = jasmine.getGlobal().console; if (console && console.log) { if (console.log.apply) { console.log.apply(console, arguments); } else { console.log(arguments); // ie fix: console.log.apply doesn't exist on ie } } }; jasmine.TrivialReporter.prototype.getLocation = function() { return this.document.location; }; jasmine.TrivialReporter.prototype.specFilter = function(spec) { var paramMap = {}; var params = this.getLocation().search.substring(1).split('&'); for (var i = 0; i < params.length; i++) { var p = params[i].split('='); paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); } if (!paramMap.spec) { return true; } return spec.getFullName().indexOf(paramMap.spec) === 0; }; ================================================ FILE: assets/test/jasmine/jasmine.css ================================================ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } #HTMLReporter a { text-decoration: none; } #HTMLReporter a:hover { text-decoration: underline; } #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } #HTMLReporter #jasmine_content { position: fixed; right: 100%; } #HTMLReporter .version { color: #aaaaaa; } #HTMLReporter .banner { margin-top: 14px; } #HTMLReporter .duration { color: #aaaaaa; float: right; } #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } #HTMLReporter .symbolSummary li.passed { font-size: 14px; } #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } #HTMLReporter .symbolSummary li.failed { line-height: 9px; } #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } #HTMLReporter .symbolSummary li.pending { line-height: 11px; } #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } #HTMLReporter .runningAlert { background-color: #666666; } #HTMLReporter .skippedAlert { background-color: #aaaaaa; } #HTMLReporter .skippedAlert:first-child { background-color: #333333; } #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } #HTMLReporter .passingAlert { background-color: #a6b779; } #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } #HTMLReporter .failingAlert { background-color: #cf867e; } #HTMLReporter .failingAlert:first-child { background-color: #b03911; } #HTMLReporter .results { margin-top: 14px; } #HTMLReporter #details { display: none; } #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } #HTMLReporter.showDetails .summary { display: none; } #HTMLReporter.showDetails #details { display: block; } #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } #HTMLReporter .summary { margin-top: 14px; } #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } #HTMLReporter .summary .specSummary.failed a { color: #b03911; } #HTMLReporter .description + .suite { margin-top: 0; } #HTMLReporter .suite { margin-top: 14px; } #HTMLReporter .suite a { color: #333333; } #HTMLReporter #details .specDetail { margin-bottom: 28px; } #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } #HTMLReporter .resultMessage span.result { display: block; } #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } #TrivialReporter a:visited, #TrivialReporter a { color: #303; } #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } #TrivialReporter .runner.running { background-color: yellow; } #TrivialReporter .options { text-align: right; font-size: .8em; } #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } #TrivialReporter .suite .suite { margin: 5px; } #TrivialReporter .suite.passed { background-color: #dfd; } #TrivialReporter .suite.failed { background-color: #fdd; } #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } #TrivialReporter .spec.skipped { background-color: #bbb; } #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } #TrivialReporter .passed { background-color: #cfc; display: none; } #TrivialReporter .failed { background-color: #fbb; } #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } #TrivialReporter .resultMessage .mismatch { color: black; } #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } #TrivialReporter #jasmine_content { position: fixed; right: 100%; } #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } ================================================ FILE: assets/test/jasmine/jasmine.js ================================================ var isCommonJS = typeof window == "undefined" && typeof exports == "object"; /** * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. * * @namespace */ var jasmine = {}; if (isCommonJS) exports.jasmine = jasmine; /** * @private */ jasmine.unimplementedMethod_ = function() { throw new Error("unimplemented method"); }; /** * Use jasmine.undefined instead of undefined, since undefined is just * a plain old variable and may be redefined by somebody else. * * @private */ jasmine.undefined = jasmine.___undefined___; /** * Show diagnostic messages in the console if set to true * */ jasmine.VERBOSE = false; /** * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. * */ jasmine.DEFAULT_UPDATE_INTERVAL = 250; /** * Maximum levels of nesting that will be included when an object is pretty-printed */ jasmine.MAX_PRETTY_PRINT_DEPTH = 40; /** * Default timeout interval in milliseconds for waitsFor() blocks. */ jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; /** * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. * Set to false to let the exception bubble up in the browser. * */ jasmine.CATCH_EXCEPTIONS = true; jasmine.getGlobal = function() { function getGlobal() { return this; } return getGlobal(); }; /** * Allows for bound functions to be compared. Internal use only. * * @ignore * @private * @param base {Object} bound 'this' for the function * @param name {Function} function to find */ jasmine.bindOriginal_ = function(base, name) { var original = base[name]; if (original.apply) { return function() { return original.apply(base, arguments); }; } else { // IE support return jasmine.getGlobal()[name]; } }; jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); jasmine.MessageResult = function(values) { this.type = 'log'; this.values = values; this.trace = new Error(); // todo: test better }; jasmine.MessageResult.prototype.toString = function() { var text = ""; for (var i = 0; i < this.values.length; i++) { if (i > 0) text += " "; if (jasmine.isString_(this.values[i])) { text += this.values[i]; } else { text += jasmine.pp(this.values[i]); } } return text; }; jasmine.ExpectationResult = function(params) { this.type = 'expect'; this.matcherName = params.matcherName; this.passed_ = params.passed; this.expected = params.expected; this.actual = params.actual; this.message = this.passed_ ? 'Passed.' : params.message; var trace = (params.trace || new Error(this.message)); this.trace = this.passed_ ? '' : trace; }; jasmine.ExpectationResult.prototype.toString = function () { return this.message; }; jasmine.ExpectationResult.prototype.passed = function () { return this.passed_; }; /** * Getter for the Jasmine environment. Ensures one gets created */ jasmine.getEnv = function() { var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); return env; }; /** * @ignore * @private * @param value * @returns {Boolean} */ jasmine.isArray_ = function(value) { return jasmine.isA_("Array", value); }; /** * @ignore * @private * @param value * @returns {Boolean} */ jasmine.isString_ = function(value) { return jasmine.isA_("String", value); }; /** * @ignore * @private * @param value * @returns {Boolean} */ jasmine.isNumber_ = function(value) { return jasmine.isA_("Number", value); }; /** * @ignore * @private * @param {String} typeName * @param value * @returns {Boolean} */ jasmine.isA_ = function(typeName, value) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; }; /** * Pretty printer for expecations. Takes any object and turns it into a human-readable string. * * @param value {Object} an object to be outputted * @returns {String} */ jasmine.pp = function(value) { var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); stringPrettyPrinter.format(value); return stringPrettyPrinter.string; }; /** * Returns true if the object is a DOM Node. * * @param {Object} obj object to check * @returns {Boolean} */ jasmine.isDomNode = function(obj) { return obj.nodeType > 0; }; /** * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. * * @example * // don't care about which function is passed in, as long as it's a function * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); * * @param {Class} clazz * @returns matchable object of the type clazz */ jasmine.any = function(clazz) { return new jasmine.Matchers.Any(clazz); }; /** * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the * attributes on the object. * * @example * // don't care about any other attributes than foo. * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); * * @param sample {Object} sample * @returns matchable object for the sample */ jasmine.objectContaining = function (sample) { return new jasmine.Matchers.ObjectContaining(sample); }; /** * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. * * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine * expectation syntax. Spies can be checked if they were called or not and what the calling params were. * * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). * * Spies are torn down at the end of every spec. * * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. * * @example * // a stub * var myStub = jasmine.createSpy('myStub'); // can be used anywhere * * // spy example * var foo = { * not: function(bool) { return !bool; } * } * * // actual foo.not will not be called, execution stops * spyOn(foo, 'not'); // foo.not spied upon, execution will continue to implementation * spyOn(foo, 'not').andCallThrough(); * * // fake example * var foo = { * not: function(bool) { return !bool; } * } * * // foo.not(val) will return val * spyOn(foo, 'not').andCallFake(function(value) {return value;}); * * // mock example * foo.not(7 == 7); * expect(foo.not).toHaveBeenCalled(); * expect(foo.not).toHaveBeenCalledWith(true); * * @constructor * @see spyOn, jasmine.createSpy, jasmine.createSpyObj * @param {String} name */ jasmine.Spy = function(name) { /** * The name of the spy, if provided. */ this.identity = name || 'unknown'; /** * Is this Object a spy? */ this.isSpy = true; /** * The actual function this spy stubs. */ this.plan = function() { }; /** * Tracking of the most recent call to the spy. * @example * var mySpy = jasmine.createSpy('foo'); * mySpy(1, 2); * mySpy.mostRecentCall.args = [1, 2]; */ this.mostRecentCall = {}; /** * Holds arguments for each call to the spy, indexed by call count * @example * var mySpy = jasmine.createSpy('foo'); * mySpy(1, 2); * mySpy(7, 8); * mySpy.mostRecentCall.args = [7, 8]; * mySpy.argsForCall[0] = [1, 2]; * mySpy.argsForCall[1] = [7, 8]; */ this.argsForCall = []; this.calls = []; }; /** * Tells a spy to call through to the actual implemenatation. * * @example * var foo = { * bar: function() { // do some stuff } * } * * // defining a spy on an existing property: foo.bar * spyOn(foo, 'bar').andCallThrough(); */ jasmine.Spy.prototype.andCallThrough = function() { this.plan = this.originalValue; return this; }; /** * For setting the return value of a spy. * * @example * // defining a spy from scratch: foo() returns 'baz' * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); * * // defining a spy on an existing property: foo.bar() returns 'baz' * spyOn(foo, 'bar').andReturn('baz'); * * @param {Object} value */ jasmine.Spy.prototype.andReturn = function(value) { this.plan = function() { return value; }; return this; }; /** * For throwing an exception when a spy is called. * * @example * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); * * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' * spyOn(foo, 'bar').andThrow('baz'); * * @param {String} exceptionMsg */ jasmine.Spy.prototype.andThrow = function(exceptionMsg) { this.plan = function() { throw exceptionMsg; }; return this; }; /** * Calls an alternate implementation when a spy is called. * * @example * var baz = function() { * // do some stuff, return something * } * // defining a spy from scratch: foo() calls the function baz * var foo = jasmine.createSpy('spy on foo').andCall(baz); * * // defining a spy on an existing property: foo.bar() calls an anonymnous function * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); * * @param {Function} fakeFunc */ jasmine.Spy.prototype.andCallFake = function(fakeFunc) { this.plan = fakeFunc; return this; }; /** * Resets all of a spy's the tracking variables so that it can be used again. * * @example * spyOn(foo, 'bar'); * * foo.bar(); * * expect(foo.bar.callCount).toEqual(1); * * foo.bar.reset(); * * expect(foo.bar.callCount).toEqual(0); */ jasmine.Spy.prototype.reset = function() { this.wasCalled = false; this.callCount = 0; this.argsForCall = []; this.calls = []; this.mostRecentCall = {}; }; jasmine.createSpy = function(name) { var spyObj = function() { spyObj.wasCalled = true; spyObj.callCount++; var args = jasmine.util.argsToArray(arguments); spyObj.mostRecentCall.object = this; spyObj.mostRecentCall.args = args; spyObj.argsForCall.push(args); spyObj.calls.push({object: this, args: args}); return spyObj.plan.apply(this, arguments); }; var spy = new jasmine.Spy(name); for (var prop in spy) { spyObj[prop] = spy[prop]; } spyObj.reset(); return spyObj; }; /** * Determines whether an object is a spy. * * @param {jasmine.Spy|Object} putativeSpy * @returns {Boolean} */ jasmine.isSpy = function(putativeSpy) { return putativeSpy && putativeSpy.isSpy; }; /** * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something * large in one call. * * @param {String} baseName name of spy class * @param {Array} methodNames array of names of methods to make spies */ jasmine.createSpyObj = function(baseName, methodNames) { if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); } var obj = {}; for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); } return obj; }; /** * All parameters are pretty-printed and concatenated together, then written to the current spec's output. * * Be careful not to leave calls to jasmine.log in production code. */ jasmine.log = function() { var spec = jasmine.getEnv().currentSpec; spec.log.apply(spec, arguments); }; /** * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. * * @example * // spy example * var foo = { * not: function(bool) { return !bool; } * } * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops * * @see jasmine.createSpy * @param obj * @param methodName * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods */ var spyOn = function(obj, methodName) { return jasmine.getEnv().currentSpec.spyOn(obj, methodName); }; if (isCommonJS) exports.spyOn = spyOn; /** * Creates a Jasmine spec that will be added to the current suite. * * // TODO: pending tests * * @example * it('should be true', function() { * expect(true).toEqual(true); * }); * * @param {String} desc description of this specification * @param {Function} func defines the preconditions and expectations of the spec */ var it = function(desc, func) { return jasmine.getEnv().it(desc, func); }; if (isCommonJS) exports.it = it; /** * Creates a disabled Jasmine spec. * * A convenience method that allows existing specs to be disabled temporarily during development. * * @param {String} desc description of this specification * @param {Function} func defines the preconditions and expectations of the spec */ var xit = function(desc, func) { return jasmine.getEnv().xit(desc, func); }; if (isCommonJS) exports.xit = xit; /** * Starts a chain for a Jasmine expectation. * * It is passed an Object that is the actual value and should chain to one of the many * jasmine.Matchers functions. * * @param {Object} actual Actual value to test against and expected value * @return {jasmine.Matchers} */ var expect = function(actual) { return jasmine.getEnv().currentSpec.expect(actual); }; if (isCommonJS) exports.expect = expect; /** * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. * * @param {Function} func Function that defines part of a jasmine spec. */ var runs = function(func) { jasmine.getEnv().currentSpec.runs(func); }; if (isCommonJS) exports.runs = runs; /** * Waits a fixed time period before moving to the next block. * * @deprecated Use waitsFor() instead * @param {Number} timeout milliseconds to wait */ var waits = function(timeout) { jasmine.getEnv().currentSpec.waits(timeout); }; if (isCommonJS) exports.waits = waits; /** * Waits for the latchFunction to return true before proceeding to the next block. * * @param {Function} latchFunction * @param {String} optional_timeoutMessage * @param {Number} optional_timeout */ var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); }; if (isCommonJS) exports.waitsFor = waitsFor; /** * A function that is called before each spec in a suite. * * Used for spec setup, including validating assumptions. * * @param {Function} beforeEachFunction */ var beforeEach = function(beforeEachFunction) { jasmine.getEnv().beforeEach(beforeEachFunction); }; if (isCommonJS) exports.beforeEach = beforeEach; /** * A function that is called after each spec in a suite. * * Used for restoring any state that is hijacked during spec execution. * * @param {Function} afterEachFunction */ var afterEach = function(afterEachFunction) { jasmine.getEnv().afterEach(afterEachFunction); }; if (isCommonJS) exports.afterEach = afterEach; /** * Defines a suite of specifications. * * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization * of setup in some tests. * * @example * // TODO: a simple suite * * // TODO: a simple suite with a nested describe block * * @param {String} description A string, usually the class under test. * @param {Function} specDefinitions function that defines several specs. */ var describe = function(description, specDefinitions) { return jasmine.getEnv().describe(description, specDefinitions); }; if (isCommonJS) exports.describe = describe; /** * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. * * @param {String} description A string, usually the class under test. * @param {Function} specDefinitions function that defines several specs. */ var xdescribe = function(description, specDefinitions) { return jasmine.getEnv().xdescribe(description, specDefinitions); }; if (isCommonJS) exports.xdescribe = xdescribe; // Provide the XMLHttpRequest class for IE 5.x-6.x: jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { function tryIt(f) { try { return f(); } catch(e) { } return null; } var xhr = tryIt(function() { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }) || tryIt(function() { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }) || tryIt(function() { return new ActiveXObject("Msxml2.XMLHTTP"); }) || tryIt(function() { return new ActiveXObject("Microsoft.XMLHTTP"); }); if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); return xhr; } : XMLHttpRequest; /** * @namespace */ jasmine.util = {}; /** * Declare that a child class inherit it's prototype from the parent class. * * @private * @param {Function} childClass * @param {Function} parentClass */ jasmine.util.inherit = function(childClass, parentClass) { /** * @private */ var subclass = function() { }; subclass.prototype = parentClass.prototype; childClass.prototype = new subclass(); }; jasmine.util.formatException = function(e) { var lineNumber; if (e.line) { lineNumber = e.line; } else if (e.lineNumber) { lineNumber = e.lineNumber; } var file; if (e.sourceURL) { file = e.sourceURL; } else if (e.fileName) { file = e.fileName; } var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); if (file && lineNumber) { message += ' in ' + file + ' (line ' + lineNumber + ')'; } return message; }; jasmine.util.htmlEscape = function(str) { if (!str) return str; return str.replace(/&/g, '&') .replace(//g, '>'); }; jasmine.util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); return arrayOfArgs; }; jasmine.util.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; }; /** * Environment for Jasmine * * @constructor */ jasmine.Env = function() { this.currentSpec = null; this.currentSuite = null; this.currentRunner_ = new jasmine.Runner(this); this.reporter = new jasmine.MultiReporter(); this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; this.lastUpdate = 0; this.specFilter = function() { return true; }; this.nextSpecId_ = 0; this.nextSuiteId_ = 0; this.equalityTesters_ = []; // wrap matchers this.matchersClass = function() { jasmine.Matchers.apply(this, arguments); }; jasmine.util.inherit(this.matchersClass, jasmine.Matchers); jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); }; jasmine.Env.prototype.setTimeout = jasmine.setTimeout; jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; jasmine.Env.prototype.setInterval = jasmine.setInterval; jasmine.Env.prototype.clearInterval = jasmine.clearInterval; /** * @returns an object containing jasmine version build info, if set. */ jasmine.Env.prototype.version = function () { if (jasmine.version_) { return jasmine.version_; } else { throw new Error('Version not set'); } }; /** * @returns string containing jasmine version build info, if set. */ jasmine.Env.prototype.versionString = function() { if (!jasmine.version_) { return "version unknown"; } var version = this.version(); var versionString = version.major + "." + version.minor + "." + version.build; if (version.release_candidate) { versionString += ".rc" + version.release_candidate; } versionString += " revision " + version.revision; return versionString; }; /** * @returns a sequential integer starting at 0 */ jasmine.Env.prototype.nextSpecId = function () { return this.nextSpecId_++; }; /** * @returns a sequential integer starting at 0 */ jasmine.Env.prototype.nextSuiteId = function () { return this.nextSuiteId_++; }; /** * Register a reporter to receive status updates from Jasmine. * @param {jasmine.Reporter} reporter An object which will receive status updates. */ jasmine.Env.prototype.addReporter = function(reporter) { this.reporter.addReporter(reporter); }; jasmine.Env.prototype.execute = function() { this.currentRunner_.execute(); }; jasmine.Env.prototype.describe = function(description, specDefinitions) { var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); var parentSuite = this.currentSuite; if (parentSuite) { parentSuite.add(suite); } else { this.currentRunner_.add(suite); } this.currentSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch(e) { declarationError = e; } if (declarationError) { this.it("encountered a declaration exception", function() { throw declarationError; }); } this.currentSuite = parentSuite; return suite; }; jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { if (this.currentSuite) { this.currentSuite.beforeEach(beforeEachFunction); } else { this.currentRunner_.beforeEach(beforeEachFunction); } }; jasmine.Env.prototype.currentRunner = function () { return this.currentRunner_; }; jasmine.Env.prototype.afterEach = function(afterEachFunction) { if (this.currentSuite) { this.currentSuite.afterEach(afterEachFunction); } else { this.currentRunner_.afterEach(afterEachFunction); } }; jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { return { execute: function() { } }; }; jasmine.Env.prototype.it = function(description, func) { var spec = new jasmine.Spec(this, this.currentSuite, description); this.currentSuite.add(spec); this.currentSpec = spec; if (func) { spec.runs(func); } return spec; }; jasmine.Env.prototype.xit = function(desc, func) { return { id: this.nextSpecId(), runs: function() { } }; }; jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { if (a.source != b.source) mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); if (a.ignoreCase != b.ignoreCase) mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); if (a.global != b.global) mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); if (a.multiline != b.multiline) mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); if (a.sticky != b.sticky) mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); return (mismatchValues.length === 0); }; jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { return true; } a.__Jasmine_been_here_before__ = b; b.__Jasmine_been_here_before__ = a; var hasKey = function(obj, keyName) { return obj !== null && obj[keyName] !== jasmine.undefined; }; for (var property in b) { if (!hasKey(a, property) && hasKey(b, property)) { mismatchKeys.push("expected has key '" + property + "', but missing from actual."); } } for (property in a) { if (!hasKey(b, property) && hasKey(a, property)) { mismatchKeys.push("expected missing key '" + property + "', but present in actual."); } } for (property in b) { if (property == '__Jasmine_been_here_before__') continue; if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); } } if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { mismatchValues.push("arrays were not the same length"); } delete a.__Jasmine_been_here_before__; delete b.__Jasmine_been_here_before__; return (mismatchKeys.length === 0 && mismatchValues.length === 0); }; jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { mismatchKeys = mismatchKeys || []; mismatchValues = mismatchValues || []; for (var i = 0; i < this.equalityTesters_.length; i++) { var equalityTester = this.equalityTesters_[i]; var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); if (result !== jasmine.undefined) return result; } if (a === b) return true; if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { return (a == jasmine.undefined && b == jasmine.undefined); } if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { return a === b; } if (a instanceof Date && b instanceof Date) { return a.getTime() == b.getTime(); } if (a.jasmineMatches) { return a.jasmineMatches(b); } if (b.jasmineMatches) { return b.jasmineMatches(a); } if (a instanceof jasmine.Matchers.ObjectContaining) { return a.matches(b); } if (b instanceof jasmine.Matchers.ObjectContaining) { return b.matches(a); } if (jasmine.isString_(a) && jasmine.isString_(b)) { return (a == b); } if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { return (a == b); } if (a instanceof RegExp && b instanceof RegExp) { return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); } if (typeof a === "object" && typeof b === "object") { return this.compareObjects_(a, b, mismatchKeys, mismatchValues); } //Straight check return (a === b); }; jasmine.Env.prototype.contains_ = function(haystack, needle) { if (jasmine.isArray_(haystack)) { for (var i = 0; i < haystack.length; i++) { if (this.equals_(haystack[i], needle)) return true; } return false; } return haystack.indexOf(needle) >= 0; }; jasmine.Env.prototype.addEqualityTester = function(equalityTester) { this.equalityTesters_.push(equalityTester); }; /** No-op base class for Jasmine reporters. * * @constructor */ jasmine.Reporter = function() { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportRunnerResults = function(runner) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportSuiteResults = function(suite) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportSpecStarting = function(spec) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportSpecResults = function(spec) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.log = function(str) { }; /** * Blocks are functions with executable code that make up a spec. * * @constructor * @param {jasmine.Env} env * @param {Function} func * @param {jasmine.Spec} spec */ jasmine.Block = function(env, func, spec) { this.env = env; this.func = func; this.spec = spec; }; jasmine.Block.prototype.execute = function(onComplete) { if (!jasmine.CATCH_EXCEPTIONS) { this.func.apply(this.spec); } else { try { this.func.apply(this.spec); } catch (e) { this.spec.fail(e); } } onComplete(); }; /** JavaScript API reporter. * * @constructor */ jasmine.JsApiReporter = function() { this.started = false; this.finished = false; this.suites_ = []; this.results_ = {}; }; jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { this.started = true; var suites = runner.topLevelSuites(); for (var i = 0; i < suites.length; i++) { var suite = suites[i]; this.suites_.push(this.summarize_(suite)); } }; jasmine.JsApiReporter.prototype.suites = function() { return this.suites_; }; jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { var isSuite = suiteOrSpec instanceof jasmine.Suite; var summary = { id: suiteOrSpec.id, name: suiteOrSpec.description, type: isSuite ? 'suite' : 'spec', children: [] }; if (isSuite) { var children = suiteOrSpec.children(); for (var i = 0; i < children.length; i++) { summary.children.push(this.summarize_(children[i])); } } return summary; }; jasmine.JsApiReporter.prototype.results = function() { return this.results_; }; jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { return this.results_[specId]; }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { this.finished = true; }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { this.results_[spec.id] = { messages: spec.results().getItems(), result: spec.results().failedCount > 0 ? "failed" : "passed" }; }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.log = function(str) { }; jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ var results = {}; for (var i = 0; i < specIds.length; i++) { var specId = specIds[i]; results[specId] = this.summarizeResult_(this.results_[specId]); } return results; }; jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ var summaryMessages = []; var messagesLength = result.messages.length; for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { var resultMessage = result.messages[messageIndex]; summaryMessages.push({ text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, passed: resultMessage.passed ? resultMessage.passed() : true, type: resultMessage.type, message: resultMessage.message, trace: { stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined } }); } return { result : result.result, messages : summaryMessages }; }; /** * @constructor * @param {jasmine.Env} env * @param actual * @param {jasmine.Spec} spec */ jasmine.Matchers = function(env, actual, spec, opt_isNot) { this.env = env; this.actual = actual; this.spec = spec; this.isNot = opt_isNot || false; this.reportWasCalled_ = false; }; // todo: @deprecated as of Jasmine 0.11, remove soon [xw] jasmine.Matchers.pp = function(str) { throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); }; // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] jasmine.Matchers.prototype.report = function(result, failing_message, details) { throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); }; jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { for (var methodName in prototype) { if (methodName == 'report') continue; var orig = prototype[methodName]; matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); } }; jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { return function() { var matcherArgs = jasmine.util.argsToArray(arguments); var result = matcherFunction.apply(this, arguments); if (this.isNot) { result = !result; } if (this.reportWasCalled_) return result; var message; if (!result) { if (this.message) { message = this.message.apply(this, arguments); if (jasmine.isArray_(message)) { message = message[this.isNot ? 1 : 0]; } } else { var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; if (matcherArgs.length > 0) { for (var i = 0; i < matcherArgs.length; i++) { if (i > 0) message += ","; message += " " + jasmine.pp(matcherArgs[i]); } } message += "."; } } var expectationResult = new jasmine.ExpectationResult({ matcherName: matcherName, passed: result, expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], actual: this.actual, message: message }); this.spec.addMatcherResult(expectationResult); return jasmine.undefined; }; }; /** * toBe: compares the actual to the expected using === * @param expected */ jasmine.Matchers.prototype.toBe = function(expected) { return this.actual === expected; }; /** * toNotBe: compares the actual to the expected using !== * @param expected * @deprecated as of 1.0. Use not.toBe() instead. */ jasmine.Matchers.prototype.toNotBe = function(expected) { return this.actual !== expected; }; /** * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. * * @param expected */ jasmine.Matchers.prototype.toEqual = function(expected) { return this.env.equals_(this.actual, expected); }; /** * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual * @param expected * @deprecated as of 1.0. Use not.toEqual() instead. */ jasmine.Matchers.prototype.toNotEqual = function(expected) { return !this.env.equals_(this.actual, expected); }; /** * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes * a pattern or a String. * * @param expected */ jasmine.Matchers.prototype.toMatch = function(expected) { return new RegExp(expected).test(this.actual); }; /** * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch * @param expected * @deprecated as of 1.0. Use not.toMatch() instead. */ jasmine.Matchers.prototype.toNotMatch = function(expected) { return !(new RegExp(expected).test(this.actual)); }; /** * Matcher that compares the actual to jasmine.undefined. */ jasmine.Matchers.prototype.toBeDefined = function() { return (this.actual !== jasmine.undefined); }; /** * Matcher that compares the actual to jasmine.undefined. */ jasmine.Matchers.prototype.toBeUndefined = function() { return (this.actual === jasmine.undefined); }; /** * Matcher that compares the actual to null. */ jasmine.Matchers.prototype.toBeNull = function() { return (this.actual === null); }; /** * Matcher that compares the actual to NaN. */ jasmine.Matchers.prototype.toBeNaN = function() { this.message = function() { return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; }; return (this.actual !== this.actual); }; /** * Matcher that boolean not-nots the actual. */ jasmine.Matchers.prototype.toBeTruthy = function() { return !!this.actual; }; /** * Matcher that boolean nots the actual. */ jasmine.Matchers.prototype.toBeFalsy = function() { return !this.actual; }; /** * Matcher that checks to see if the actual, a Jasmine spy, was called. */ jasmine.Matchers.prototype.toHaveBeenCalled = function() { if (arguments.length > 0) { throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); } if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { return [ "Expected spy " + this.actual.identity + " to have been called.", "Expected spy " + this.actual.identity + " not to have been called." ]; }; return this.actual.wasCalled; }; /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; /** * Matcher that checks to see if the actual, a Jasmine spy, was not called. * * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead */ jasmine.Matchers.prototype.wasNotCalled = function() { if (arguments.length > 0) { throw new Error('wasNotCalled does not take arguments'); } if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { return [ "Expected spy " + this.actual.identity + " to not have been called.", "Expected spy " + this.actual.identity + " to have been called." ]; }; return !this.actual.wasCalled; }; /** * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. * * @example * */ jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; var positiveMessage = ""; if (this.actual.callCount === 0) { positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; } else { positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') } return [positiveMessage, invertedMessage]; }; return this.env.contains_(this.actual.argsForCall, expectedArgs); }; /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ jasmine.Matchers.prototype.wasNotCalledWith = function() { var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { return [ "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" ]; }; return !this.env.contains_(this.actual.argsForCall, expectedArgs); }; /** * Matcher that checks that the expected item is an element in the actual Array. * * @param {Object} expected */ jasmine.Matchers.prototype.toContain = function(expected) { return this.env.contains_(this.actual, expected); }; /** * Matcher that checks that the expected item is NOT an element in the actual Array. * * @param {Object} expected * @deprecated as of 1.0. Use not.toContain() instead. */ jasmine.Matchers.prototype.toNotContain = function(expected) { return !this.env.contains_(this.actual, expected); }; jasmine.Matchers.prototype.toBeLessThan = function(expected) { return this.actual < expected; }; jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { return this.actual > expected; }; /** * Matcher that checks that the expected item is equal to the actual item * up to a given level of decimal precision (default 2). * * @param {Number} expected * @param {Number} precision, as number of decimal places */ jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { if (!(precision === 0)) { precision = precision || 2; } return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); }; /** * Matcher that checks that the expected exception was thrown by the actual. * * @param {String} [expected] */ jasmine.Matchers.prototype.toThrow = function(expected) { var result = false; var exception; if (typeof this.actual != 'function') { throw new Error('Actual is not a function'); } try { this.actual(); } catch (e) { exception = e; } if (exception) { result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); } var not = this.isNot ? "not " : ""; this.message = function() { if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); } else { return "Expected function to throw an exception."; } }; return result; }; jasmine.Matchers.Any = function(expectedClass) { this.expectedClass = expectedClass; }; jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { if (this.expectedClass == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedClass == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedClass == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedClass == Object) { return typeof other == 'object'; } return other instanceof this.expectedClass; }; jasmine.Matchers.Any.prototype.jasmineToString = function() { return ''; }; jasmine.Matchers.ObjectContaining = function (sample) { this.sample = sample; }; jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { mismatchKeys = mismatchKeys || []; mismatchValues = mismatchValues || []; var env = jasmine.getEnv(); var hasKey = function(obj, keyName) { return obj != null && obj[keyName] !== jasmine.undefined; }; for (var property in this.sample) { if (!hasKey(other, property) && hasKey(this.sample, property)) { mismatchKeys.push("expected has key '" + property + "', but missing from actual."); } else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); } } return (mismatchKeys.length === 0 && mismatchValues.length === 0); }; jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { return ""; }; // Mock setTimeout, clearTimeout // Contributed by Pivotal Computer Systems, www.pivotalsf.com jasmine.FakeTimer = function() { this.reset(); var self = this; self.setTimeout = function(funcToCall, millis) { self.timeoutsMade++; self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); return self.timeoutsMade; }; self.setInterval = function(funcToCall, millis) { self.timeoutsMade++; self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); return self.timeoutsMade; }; self.clearTimeout = function(timeoutKey) { self.scheduledFunctions[timeoutKey] = jasmine.undefined; }; self.clearInterval = function(timeoutKey) { self.scheduledFunctions[timeoutKey] = jasmine.undefined; }; }; jasmine.FakeTimer.prototype.reset = function() { this.timeoutsMade = 0; this.scheduledFunctions = {}; this.nowMillis = 0; }; jasmine.FakeTimer.prototype.tick = function(millis) { var oldMillis = this.nowMillis; var newMillis = oldMillis + millis; this.runFunctionsWithinRange(oldMillis, newMillis); this.nowMillis = newMillis; }; jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { var scheduledFunc; var funcsToRun = []; for (var timeoutKey in this.scheduledFunctions) { scheduledFunc = this.scheduledFunctions[timeoutKey]; if (scheduledFunc != jasmine.undefined && scheduledFunc.runAtMillis >= oldMillis && scheduledFunc.runAtMillis <= nowMillis) { funcsToRun.push(scheduledFunc); this.scheduledFunctions[timeoutKey] = jasmine.undefined; } } if (funcsToRun.length > 0) { funcsToRun.sort(function(a, b) { return a.runAtMillis - b.runAtMillis; }); for (var i = 0; i < funcsToRun.length; ++i) { try { var funcToRun = funcsToRun[i]; this.nowMillis = funcToRun.runAtMillis; funcToRun.funcToCall(); if (funcToRun.recurring) { this.scheduleFunction(funcToRun.timeoutKey, funcToRun.funcToCall, funcToRun.millis, true); } } catch(e) { } } this.runFunctionsWithinRange(oldMillis, nowMillis); } }; jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { this.scheduledFunctions[timeoutKey] = { runAtMillis: this.nowMillis + millis, funcToCall: funcToCall, recurring: recurring, timeoutKey: timeoutKey, millis: millis }; }; /** * @namespace */ jasmine.Clock = { defaultFakeTimer: new jasmine.FakeTimer(), reset: function() { jasmine.Clock.assertInstalled(); jasmine.Clock.defaultFakeTimer.reset(); }, tick: function(millis) { jasmine.Clock.assertInstalled(); jasmine.Clock.defaultFakeTimer.tick(millis); }, runFunctionsWithinRange: function(oldMillis, nowMillis) { jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); }, scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); }, useMock: function() { if (!jasmine.Clock.isInstalled()) { var spec = jasmine.getEnv().currentSpec; spec.after(jasmine.Clock.uninstallMock); jasmine.Clock.installMock(); } }, installMock: function() { jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; }, uninstallMock: function() { jasmine.Clock.assertInstalled(); jasmine.Clock.installed = jasmine.Clock.real; }, real: { setTimeout: jasmine.getGlobal().setTimeout, clearTimeout: jasmine.getGlobal().clearTimeout, setInterval: jasmine.getGlobal().setInterval, clearInterval: jasmine.getGlobal().clearInterval }, assertInstalled: function() { if (!jasmine.Clock.isInstalled()) { throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); } }, isInstalled: function() { return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; }, installed: null }; jasmine.Clock.installed = jasmine.Clock.real; //else for IE support jasmine.getGlobal().setTimeout = function(funcToCall, millis) { if (jasmine.Clock.installed.setTimeout.apply) { return jasmine.Clock.installed.setTimeout.apply(this, arguments); } else { return jasmine.Clock.installed.setTimeout(funcToCall, millis); } }; jasmine.getGlobal().setInterval = function(funcToCall, millis) { if (jasmine.Clock.installed.setInterval.apply) { return jasmine.Clock.installed.setInterval.apply(this, arguments); } else { return jasmine.Clock.installed.setInterval(funcToCall, millis); } }; jasmine.getGlobal().clearTimeout = function(timeoutKey) { if (jasmine.Clock.installed.clearTimeout.apply) { return jasmine.Clock.installed.clearTimeout.apply(this, arguments); } else { return jasmine.Clock.installed.clearTimeout(timeoutKey); } }; jasmine.getGlobal().clearInterval = function(timeoutKey) { if (jasmine.Clock.installed.clearTimeout.apply) { return jasmine.Clock.installed.clearInterval.apply(this, arguments); } else { return jasmine.Clock.installed.clearInterval(timeoutKey); } }; /** * @constructor */ jasmine.MultiReporter = function() { this.subReporters_ = []; }; jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); jasmine.MultiReporter.prototype.addReporter = function(reporter) { this.subReporters_.push(reporter); }; (function() { var functionNames = [ "reportRunnerStarting", "reportRunnerResults", "reportSuiteResults", "reportSpecStarting", "reportSpecResults", "log" ]; for (var i = 0; i < functionNames.length; i++) { var functionName = functionNames[i]; jasmine.MultiReporter.prototype[functionName] = (function(functionName) { return function() { for (var j = 0; j < this.subReporters_.length; j++) { var subReporter = this.subReporters_[j]; if (subReporter[functionName]) { subReporter[functionName].apply(subReporter, arguments); } } }; })(functionName); } })(); /** * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults * * @constructor */ jasmine.NestedResults = function() { /** * The total count of results */ this.totalCount = 0; /** * Number of passed results */ this.passedCount = 0; /** * Number of failed results */ this.failedCount = 0; /** * Was this suite/spec skipped? */ this.skipped = false; /** * @ignore */ this.items_ = []; }; /** * Roll up the result counts. * * @param result */ jasmine.NestedResults.prototype.rollupCounts = function(result) { this.totalCount += result.totalCount; this.passedCount += result.passedCount; this.failedCount += result.failedCount; }; /** * Adds a log message. * @param values Array of message parts which will be concatenated later. */ jasmine.NestedResults.prototype.log = function(values) { this.items_.push(new jasmine.MessageResult(values)); }; /** * Getter for the results: message & results. */ jasmine.NestedResults.prototype.getItems = function() { return this.items_; }; /** * Adds a result, tracking counts (total, passed, & failed) * @param {jasmine.ExpectationResult|jasmine.NestedResults} result */ jasmine.NestedResults.prototype.addResult = function(result) { if (result.type != 'log') { if (result.items_) { this.rollupCounts(result); } else { this.totalCount++; if (result.passed()) { this.passedCount++; } else { this.failedCount++; } } } this.items_.push(result); }; /** * @returns {Boolean} True if everything below passed */ jasmine.NestedResults.prototype.passed = function() { return this.passedCount === this.totalCount; }; /** * Base class for pretty printing for expectation results. */ jasmine.PrettyPrinter = function() { this.ppNestLevel_ = 0; }; /** * Formats a value in a nice, human-readable string. * * @param value */ jasmine.PrettyPrinter.prototype.format = function(value) { this.ppNestLevel_++; try { if (value === jasmine.undefined) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === jasmine.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString()); } else if (typeof value === 'string') { this.emitString(value); } else if (jasmine.isSpy(value)) { this.emitScalar("spy on " + value.identity); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (typeof value.nodeType === 'number') { this.emitScalar('HTMLNode'); } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (value.__Jasmine_been_here_before__) { this.emitScalar(''); } else if (jasmine.isArray_(value) || typeof value == 'object') { value.__Jasmine_been_here_before__ = true; if (jasmine.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } delete value.__Jasmine_been_here_before__; } else { this.emitScalar(value.toString()); } } finally { this.ppNestLevel_--; } }; jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!obj.hasOwnProperty(property)) continue; if (property == '__Jasmine_been_here_before__') continue; fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && obj.__lookupGetter__(property) !== null) : false); } }; jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; jasmine.StringPrettyPrinter = function() { jasmine.PrettyPrinter.call(this); this.string = ''; }; jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { this.append(value); }; jasmine.StringPrettyPrinter.prototype.emitString = function(value) { this.append("'" + value + "'"); }; jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { this.append("Array"); return; } this.append('[ '); for (var i = 0; i < array.length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } this.append(' ]'); }; jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { this.append("Object"); return; } var self = this; this.append('{ '); var first = true; this.iterateObject(obj, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.append(property); self.append(' : '); if (isGetter) { self.append(''); } else { self.format(obj[property]); } }); this.append(' }'); }; jasmine.StringPrettyPrinter.prototype.append = function(value) { this.string += value; }; jasmine.Queue = function(env) { this.env = env; // parallel to blocks. each true value in this array means the block will // get executed even if we abort this.ensured = []; this.blocks = []; this.running = false; this.index = 0; this.offset = 0; this.abort = false; }; jasmine.Queue.prototype.addBefore = function(block, ensure) { if (ensure === jasmine.undefined) { ensure = false; } this.blocks.unshift(block); this.ensured.unshift(ensure); }; jasmine.Queue.prototype.add = function(block, ensure) { if (ensure === jasmine.undefined) { ensure = false; } this.blocks.push(block); this.ensured.push(ensure); }; jasmine.Queue.prototype.insertNext = function(block, ensure) { if (ensure === jasmine.undefined) { ensure = false; } this.ensured.splice((this.index + this.offset + 1), 0, ensure); this.blocks.splice((this.index + this.offset + 1), 0, block); this.offset++; }; jasmine.Queue.prototype.start = function(onComplete) { this.running = true; this.onComplete = onComplete; this.next_(); }; jasmine.Queue.prototype.isRunning = function() { return this.running; }; jasmine.Queue.LOOP_DONT_RECURSE = true; jasmine.Queue.prototype.next_ = function() { var self = this; var goAgain = true; while (goAgain) { goAgain = false; if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { var calledSynchronously = true; var completedSynchronously = false; var onComplete = function () { if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { completedSynchronously = true; return; } if (self.blocks[self.index].abort) { self.abort = true; } self.offset = 0; self.index++; var now = new Date().getTime(); if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { self.env.lastUpdate = now; self.env.setTimeout(function() { self.next_(); }, 0); } else { if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { goAgain = true; } else { self.next_(); } } }; self.blocks[self.index].execute(onComplete); calledSynchronously = false; if (completedSynchronously) { onComplete(); } } else { self.running = false; if (self.onComplete) { self.onComplete(); } } } }; jasmine.Queue.prototype.results = function() { var results = new jasmine.NestedResults(); for (var i = 0; i < this.blocks.length; i++) { if (this.blocks[i].results) { results.addResult(this.blocks[i].results()); } } return results; }; /** * Runner * * @constructor * @param {jasmine.Env} env */ jasmine.Runner = function(env) { var self = this; self.env = env; self.queue = new jasmine.Queue(env); self.before_ = []; self.after_ = []; self.suites_ = []; }; jasmine.Runner.prototype.execute = function() { var self = this; if (self.env.reporter.reportRunnerStarting) { self.env.reporter.reportRunnerStarting(this); } self.queue.start(function () { self.finishCallback(); }); }; jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { beforeEachFunction.typeName = 'beforeEach'; this.before_.splice(0,0,beforeEachFunction); }; jasmine.Runner.prototype.afterEach = function(afterEachFunction) { afterEachFunction.typeName = 'afterEach'; this.after_.splice(0,0,afterEachFunction); }; jasmine.Runner.prototype.finishCallback = function() { this.env.reporter.reportRunnerResults(this); }; jasmine.Runner.prototype.addSuite = function(suite) { this.suites_.push(suite); }; jasmine.Runner.prototype.add = function(block) { if (block instanceof jasmine.Suite) { this.addSuite(block); } this.queue.add(block); }; jasmine.Runner.prototype.specs = function () { var suites = this.suites(); var specs = []; for (var i = 0; i < suites.length; i++) { specs = specs.concat(suites[i].specs()); } return specs; }; jasmine.Runner.prototype.suites = function() { return this.suites_; }; jasmine.Runner.prototype.topLevelSuites = function() { var topLevelSuites = []; for (var i = 0; i < this.suites_.length; i++) { if (!this.suites_[i].parentSuite) { topLevelSuites.push(this.suites_[i]); } } return topLevelSuites; }; jasmine.Runner.prototype.results = function() { return this.queue.results(); }; /** * Internal representation of a Jasmine specification, or test. * * @constructor * @param {jasmine.Env} env * @param {jasmine.Suite} suite * @param {String} description */ jasmine.Spec = function(env, suite, description) { if (!env) { throw new Error('jasmine.Env() required'); } if (!suite) { throw new Error('jasmine.Suite() required'); } var spec = this; spec.id = env.nextSpecId ? env.nextSpecId() : null; spec.env = env; spec.suite = suite; spec.description = description; spec.queue = new jasmine.Queue(env); spec.afterCallbacks = []; spec.spies_ = []; spec.results_ = new jasmine.NestedResults(); spec.results_.description = description; spec.matchersClass = null; }; jasmine.Spec.prototype.getFullName = function() { return this.suite.getFullName() + ' ' + this.description + '.'; }; jasmine.Spec.prototype.results = function() { return this.results_; }; /** * All parameters are pretty-printed and concatenated together, then written to the spec's output. * * Be careful not to leave calls to jasmine.log in production code. */ jasmine.Spec.prototype.log = function() { return this.results_.log(arguments); }; jasmine.Spec.prototype.runs = function (func) { var block = new jasmine.Block(this.env, func, this); this.addToQueue(block); return this; }; jasmine.Spec.prototype.addToQueue = function (block) { if (this.queue.isRunning()) { this.queue.insertNext(block); } else { this.queue.add(block); } }; /** * @param {jasmine.ExpectationResult} result */ jasmine.Spec.prototype.addMatcherResult = function(result) { this.results_.addResult(result); }; jasmine.Spec.prototype.expect = function(actual) { var positive = new (this.getMatchersClass_())(this.env, actual, this); positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); return positive; }; /** * Waits a fixed time period before moving to the next block. * * @deprecated Use waitsFor() instead * @param {Number} timeout milliseconds to wait */ jasmine.Spec.prototype.waits = function(timeout) { var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); this.addToQueue(waitsFunc); return this; }; /** * Waits for the latchFunction to return true before proceeding to the next block. * * @param {Function} latchFunction * @param {String} optional_timeoutMessage * @param {Number} optional_timeout */ jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { var latchFunction_ = null; var optional_timeoutMessage_ = null; var optional_timeout_ = null; for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; switch (typeof arg) { case 'function': latchFunction_ = arg; break; case 'string': optional_timeoutMessage_ = arg; break; case 'number': optional_timeout_ = arg; break; } } var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); this.addToQueue(waitsForFunc); return this; }; jasmine.Spec.prototype.fail = function (e) { var expectationResult = new jasmine.ExpectationResult({ passed: false, message: e ? jasmine.util.formatException(e) : 'Exception', trace: { stack: e.stack } }); this.results_.addResult(expectationResult); }; jasmine.Spec.prototype.getMatchersClass_ = function() { return this.matchersClass || this.env.matchersClass; }; jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { var parent = this.getMatchersClass_(); var newMatchersClass = function() { parent.apply(this, arguments); }; jasmine.util.inherit(newMatchersClass, parent); jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); this.matchersClass = newMatchersClass; }; jasmine.Spec.prototype.finishCallback = function() { this.env.reporter.reportSpecResults(this); }; jasmine.Spec.prototype.finish = function(onComplete) { this.removeAllSpies(); this.finishCallback(); if (onComplete) { onComplete(); } }; jasmine.Spec.prototype.after = function(doAfter) { if (this.queue.isRunning()) { this.queue.add(new jasmine.Block(this.env, doAfter, this), true); } else { this.afterCallbacks.unshift(doAfter); } }; jasmine.Spec.prototype.execute = function(onComplete) { var spec = this; if (!spec.env.specFilter(spec)) { spec.results_.skipped = true; spec.finish(onComplete); return; } this.env.reporter.reportSpecStarting(this); spec.env.currentSpec = spec; spec.addBeforesAndAftersToQueue(); spec.queue.start(function () { spec.finish(onComplete); }); }; jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { var runner = this.env.currentRunner(); var i; for (var suite = this.suite; suite; suite = suite.parentSuite) { for (i = 0; i < suite.before_.length; i++) { this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); } } for (i = 0; i < runner.before_.length; i++) { this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); } for (i = 0; i < this.afterCallbacks.length; i++) { this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); } for (suite = this.suite; suite; suite = suite.parentSuite) { for (i = 0; i < suite.after_.length; i++) { this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); } } for (i = 0; i < runner.after_.length; i++) { this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); } }; jasmine.Spec.prototype.explodes = function() { throw 'explodes function should not have been called'; }; jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { if (obj == jasmine.undefined) { throw "spyOn could not find an object to spy upon for " + methodName + "()"; } if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { throw methodName + '() method does not exist'; } if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { throw new Error(methodName + ' has already been spied upon'); } var spyObj = jasmine.createSpy(methodName); this.spies_.push(spyObj); spyObj.baseObj = obj; spyObj.methodName = methodName; spyObj.originalValue = obj[methodName]; obj[methodName] = spyObj; return spyObj; }; jasmine.Spec.prototype.removeAllSpies = function() { for (var i = 0; i < this.spies_.length; i++) { var spy = this.spies_[i]; spy.baseObj[spy.methodName] = spy.originalValue; } this.spies_ = []; }; /** * Internal representation of a Jasmine suite. * * @constructor * @param {jasmine.Env} env * @param {String} description * @param {Function} specDefinitions * @param {jasmine.Suite} parentSuite */ jasmine.Suite = function(env, description, specDefinitions, parentSuite) { var self = this; self.id = env.nextSuiteId ? env.nextSuiteId() : null; self.description = description; self.queue = new jasmine.Queue(env); self.parentSuite = parentSuite; self.env = env; self.before_ = []; self.after_ = []; self.children_ = []; self.suites_ = []; self.specs_ = []; }; jasmine.Suite.prototype.getFullName = function() { var fullName = this.description; for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { fullName = parentSuite.description + ' ' + fullName; } return fullName; }; jasmine.Suite.prototype.finish = function(onComplete) { this.env.reporter.reportSuiteResults(this); this.finished = true; if (typeof(onComplete) == 'function') { onComplete(); } }; jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { beforeEachFunction.typeName = 'beforeEach'; this.before_.unshift(beforeEachFunction); }; jasmine.Suite.prototype.afterEach = function(afterEachFunction) { afterEachFunction.typeName = 'afterEach'; this.after_.unshift(afterEachFunction); }; jasmine.Suite.prototype.results = function() { return this.queue.results(); }; jasmine.Suite.prototype.add = function(suiteOrSpec) { this.children_.push(suiteOrSpec); if (suiteOrSpec instanceof jasmine.Suite) { this.suites_.push(suiteOrSpec); this.env.currentRunner().addSuite(suiteOrSpec); } else { this.specs_.push(suiteOrSpec); } this.queue.add(suiteOrSpec); }; jasmine.Suite.prototype.specs = function() { return this.specs_; }; jasmine.Suite.prototype.suites = function() { return this.suites_; }; jasmine.Suite.prototype.children = function() { return this.children_; }; jasmine.Suite.prototype.execute = function(onComplete) { var self = this; this.queue.start(function () { self.finish(onComplete); }); }; jasmine.WaitsBlock = function(env, timeout, spec) { this.timeout = timeout; jasmine.Block.call(this, env, null, spec); }; jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); jasmine.WaitsBlock.prototype.execute = function (onComplete) { if (jasmine.VERBOSE) { this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); } this.env.setTimeout(function () { onComplete(); }, this.timeout); }; /** * A block which waits for some condition to become true, with timeout. * * @constructor * @extends jasmine.Block * @param {jasmine.Env} env The Jasmine environment. * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. * @param {Function} latchFunction A function which returns true when the desired condition has been met. * @param {String} message The message to display if the desired condition hasn't been met within the given time period. * @param {jasmine.Spec} spec The Jasmine spec. */ jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { this.timeout = timeout || env.defaultTimeoutInterval; this.latchFunction = latchFunction; this.message = message; this.totalTimeSpentWaitingForLatch = 0; jasmine.Block.call(this, env, null, spec); }; jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; jasmine.WaitsForBlock.prototype.execute = function(onComplete) { if (jasmine.VERBOSE) { this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); } var latchFunctionResult; try { latchFunctionResult = this.latchFunction.apply(this.spec); } catch (e) { this.spec.fail(e); onComplete(); return; } if (latchFunctionResult) { onComplete(); } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); this.spec.fail({ name: 'timeout', message: message }); this.abort = true; onComplete(); } else { this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; var self = this; this.env.setTimeout(function() { self.execute(onComplete); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); } }; jasmine.version_= { "major": 1, "minor": 3, "build": 1, "revision": 1354556913 }; ================================================ FILE: assets/test/js/tests.js ================================================ /*! * SourceJS - Living Style Guides Engine and Integrated Maintenance Environment for Front-end Components * @copyright 2013-2015 Sourcejs.com * @license MIT license: http://github.com/sourcejs/source/wiki/MIT-License * */ requirejs.config({ urlArgs: 'cb=' + Math.random(), /* to avoid caching */ paths: { test: '/test', jasmine: '/test/jasmine/jasmine', 'jasmine-html': '/test/jasmine/jasmine-html' }, shim: { 'jasmine': { exports: 'jasmine' }, 'jasmine-html': { deps: ['jasmine'], exports: 'jasmine' } } }); require(['jquery', 'jasmine-html'], function($, jasmine){ var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function(spec) { return htmlReporter.specFilter(spec); }; var specs = []; specs.push('test/spec/moduleSpec'); specs.push('test/spec/sectionsSpec'); specs.push('test/spec/innerNavigationSpec'); $(function(){ require(specs, function(){ jasmineEnv.execute(); }); }); }); ================================================ FILE: assets/test/spec/innerNavigationSpec.js ================================================ /** * Created by Alexey Ostrovsky. * Date: 04.04.13 * Time: 14:33 */ define(['sourceModules/innerNavigation'], function (innerNavigation) { return describe('Inner Navigation module - innerNavigation.js', function () { var iNav; var TEST_SECTION = '
      ' + '

      Первый блок

      ' + '
      ' + '
      ' + '
      ' + '
      '; beforeEach(function () { $('#sandbox').html('

      Test heading

      '); iNav = innerNavigation.createInstance(); }); afterEach(function () { $('#sandbox').html(''); }); it('injects only navigation menu in DOM after .source_header element', function () { expect($('.source_main_nav').length).toEqual(1); }); }); }); ================================================ FILE: assets/test/spec/moduleSpec.js ================================================ /** * Created by Alexey Ostrovsky. * Date: 04.04.13 * Time: 14:33 */ define(['sourceModules/module', 'source/load-options'], function (module, options) { return describe('Base module - module.js', function () { var m; beforeEach(function () { $('#sandbox').html(); m = module.createInstance(); }); it('returns object', function () { expect(Object.prototype.toString.call(m)).toEqual('[object Object]'); }); it('loadOptions method returns opctions from options.js', function () { expect(m.loadOptions()).toEqual(options); }); it('getOptions method returns options filed value', function () { var opts = {test:'get'}; m.options = opts; expect(m.getOptions()).toEqual(opts); }); it('setOptions method sets argument in options', function () { var opts = {test:'set'}; m.setOptions(opts); expect(m.options).toEqual(opts); }); // After module.js minification class is renamed to shorter value // it('getClass method returns right object class', function () { // expect(m.getClass()).toEqual('Module'); // }); it('getClass method returns right class from inherited object', function () { var obj = m.createInstance(); function NewObject(){ this.constructor = arguments.callee; // set constructor } NewObject.prototype = obj; var newObject = new NewObject(); expect(newObject.getClass()).toEqual('NewObject'); }); it('createInstance method creates the same class instance as object', function () { var obj = m.createInstance(); expect(obj.getClass()).toEqual(m.getClass()); }); it('createInstance method creates the same class instance as object inherited from other object ', function () { var obj = m.createInstance(); function NewObject(){} NewObject.prototype = obj.createInstance(); NewObject.prototype.constructor = NewObject; var newObject = new NewObject(); expect(newObject instanceof NewObject).toEqual(true); expect(newObject.constructor.name).toEqual('NewObject'); }); }); }); ================================================ FILE: assets/test/spec/sectionsSpec.js ================================================ /** * Created by Alexey Ostrovsky. * Date: 04.04.13 * Time: 14:33 */ define(['sourceModules/sections'], function (sections) { return describe('Sections count module - sections.js', function () { var TEST_SECTION = '
      ' + '

      Первый блок

      ' + '
      ' + '
      ' + '
      ' + '
      '; var MIN = 0; var MAX = 100; var rand = Math.floor(Math.random() * (MAX - MIN + 1)) + MIN; var str = ''; var s = {}; for (var i = 0; i < rand; i++) { str += TEST_SECTION; } beforeEach(function () { $('#sandbox').html(str); s = sections.createInstance(); }); afterEach(function () { s = sections.createInstance(); }); it('returns array', function () { expect(Object.prototype.toString.call(s.getSections())).toEqual('[object Array]'); }); it('getSections method returns sections field', function () { var arr = [1, 2, 3]; s.sections = arr; expect(s.getSections()).toEqual(arr); }); it('addSections method adds element to sections field', function () { arr = [1, 2, 3]; s.sections = arr; s.addSection(4); expect(s.getSections()).toEqual([1, 2, 3, 4]); }); it('getQuantity method returns sections field size', function () { expect(s.getQuantity()).toEqual(s.sections.length); }); it('scanDOM method fills sections array field with seactions from DOM tree', function () { expect(s.getQuantity()).toEqual(rand); }); }); }); ================================================ FILE: core/api/index.js ================================================ 'use strict'; var express = require('express'); var path = require('path'); var parseData = require(path.join(global.pathToApp, 'core/lib/parseData')); var utils = require(path.join(global.pathToApp, 'core/lib/utils')); var pathToApp = path.dirname(require.main.filename); var htmlTree = require(path.join(global.pathToApp, 'core/html-tree')); var unflatten = require(path.join(global.pathToApp,'core/unflat')); var config = { statusCodes: { OK: 200, notFound: 404, error: 500 } }; // Overwriting base options utils.extendOptions(config, global.opts.core.api); var specsDataPath = path.join(pathToApp, config.specsData); var htmlDataPath = path.join(pathToApp, config.htmlData); /** * getSpecs REST api processor * * @param {Object} req - express request * @param {Object} res - express response * @param {Object} parseObj - initiated parseData instance * * Writes result to res object */ var getSpecs = function (req, res, parseObj) { var data = {}; var body = req.body; var reqID = body.id || req.query.id; var cats = body.cats || req.query.cats; var reqFilter = body.filter || req.query.filter; var reqFilterOut = body.filterOut || req.query.filterOut; var parsedData = parseObj; var msgDataNotFound = 'API: Specs data not found, please restart the app.'; if (reqID) { var dataByID = parsedData.getByID(reqID); if (dataByID && typeof dataByID === 'object') { res.status(config.statusCodes.OK).json(dataByID); } else { if (typeof dataByID === 'undefined') console.warn(msgDataNotFound); res.status(config.statusCodes.notFound).json({ message: "id not found" }); } } else if (reqFilter || reqFilterOut) { var dataFiltered = parsedData.getFilteredData({ filter: reqFilter, filterOut: reqFilterOut }); if (dataFiltered && typeof dataFiltered === 'object') { res.status(config.statusCodes.OK).json(dataFiltered); } else { console.warn(msgDataNotFound); res.status(config.statusCodes.notFound).json({ message: "data not found" }); } } else { data = parsedData.getAll(cats); if (data) { res.status(config.statusCodes.OK).json(data); } else { console.warn(msgDataNotFound); res.status(config.statusCodes.notFound).json({ message: "data not found" }); } } }; /** * getHTML REST api processor * * @param {Object} req - express request * @param {Object} res - express response * @param {Object} parseObj - initiated parseData instance * * Writes result to res object */ var getHTML = function (req, res, parseObj) { var data = {}; var body = req.body; var reqID = body.id || req.query.id; var reqSections = body.sections || req.query.sections; var sections = reqSections ? reqSections.split(',') : undefined; var parsedData = parseObj; var msgDataNotFound = 'API: HTML data not found, please sync API or run PhantomJS parser.'; if (reqID) { var responseData = ''; if (reqSections) { responseData = parsedData.getBySection(reqID, sections); } else { responseData = parsedData.getByID(reqID); } if (responseData && typeof responseData === 'object') { res.status(config.statusCodes.OK).json(responseData); } else { if (typeof responseData === 'undefined') console.warn(msgDataNotFound); res.status(config.statusCodes.notFound).json({ message: "id and requested sections not found" }); } } else { data = parsedData.getAll(); if (data) { res.status(config.statusCodes.OK).json(data); } else { console.warn(msgDataNotFound); res.status(config.statusCodes.notFound).json({ message: "data not found" }); } } }; /** * postHTML REST api processor * * @param {Object} req - express request * @param {Object} req.body.data - data to write * @param {Boolean} req.body.unflatten - set true, to unflat tree from 'base/spec' * * @param {Object} res - express response * @param {String} dataPath - custom data storage path * * Writes result to res object */ var postHTML = function (req, res, dataPath) { var body = req.body; var data = body.data; var dataUnflatten = body.unflatten; if (dataUnflatten) { data = unflatten(data, { delimiter: '/', overwrite: 'root' }); } htmlTree.writeDataFile(data, true, dataPath, function(err, finalData){ if (err || !finalData) { res.status(config.statusCodes.error).json({ message: err }); } else { res.status(config.statusCodes.OK).json(finalData); } }); }; /** * postHTML DELETE api processor * * @param {Object} req - express request * @param {Object} req.body.path - data path for deletion * * @param {Object} res - express response * @param {String} dataPath - custom data storage path * * Writes result to res object */ var deleteHTML = function (req, res, dataPath) { var body = req.body; var reqID = body.id || req.query.id; htmlTree.deleteFromDataFile(dataPath, reqID, function(err, finalData){ if (err || !finalData) { res.status(config.statusCodes.error).json({ message: err }); } else { res.status(config.statusCodes.OK).json(finalData); } }); }; /** * If app is running in presentation mode, handle response with stub * * @param {Object} req - express request * @param {Object} res - express response * * Writes result to res object */ var presentationHandler = function (req, res) { res.json({ message: 'API is running in presentation mode, no write operations permited.' }); }; /* Main API router */ var apiRouter = express.Router(); var parseHTMLData = new parseData({ scope: 'html', path: htmlDataPath }); var parseSpecs = new parseData({ scope: 'specs', path: specsDataPath }); apiRouter.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET"); res.header("Access-Control-Allow-Headers", "Content-Type"); next(); }); apiRouter.get('/', function(req, res) { res.json({ message: 'Hello API' }); }); apiRouter.route('/specs/raw') .get(function (req, res) { var data = parseSpecs.getRaw(); if (data) { res.status(config.statusCodes.OK).json(data); } else { res.status(config.statusCodes.notFound).json({ message: "data not found" }); } }); apiRouter.route('/specs') .get(function (req, res) { getSpecs(req, res, parseSpecs); }); apiRouter.route('/specs/html') .get(function (req, res) { getHTML(req, res, parseHTMLData); }) .post(function (req, res) { if (global.MODE === 'presentation') { presentationHandler(req, res); } else { postHTML(req, res, htmlDataPath); } }) /* jshint es5:false */ .delete(function (req, res) { if (global.MODE === 'presentation') { presentationHandler(req, res); } else { deleteHTML(req, res, htmlDataPath); } }); // Activating router global.app.use('/api', apiRouter); /* Main API router */ /* Test API router */ // TODO: find alternative way for testing API, without custom route var apiTestRouter = express.Router(); var specsDataTestPath = path.join(pathToApp, config.specsTestData); var htmlDataTestPath = path.join(pathToApp, config.htmlTestData); var parseSpecsTest = new parseData({ scope: 'specs', path: specsDataTestPath }); var parseHTMLDataTest = new parseData({ scope: 'html', path: htmlDataTestPath }); apiTestRouter.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET"); res.header("Access-Control-Allow-Headers", "Content-Type"); next(); }); apiTestRouter.get('/', function(req, res) { res.json({ message: 'API Testig env' }); }); apiTestRouter.route('/specs') .get(function (req, res) { getSpecs(req, res, parseSpecsTest); }); apiTestRouter.route('/specs/html') .get(function (req, res) { getHTML(req, res, parseHTMLDataTest); }) .post(function (req, res) { if (global.MODE === 'presentation') { presentationHandler(req, res); } else { postHTML(req, res, htmlDataTestPath); } }) .delete(function (req, res) { if (global.MODE === 'presentation') { presentationHandler(req, res); } else { deleteHTML(req, res, htmlDataTestPath); } }); // Activating router global.app.use('/api-test', apiTestRouter); /* /Test API router */ ================================================ FILE: core/api/optionsApi.js ================================================ 'use strict'; var path = require('path'); var configUtils = require(path.join(global.pathToApp,'core/lib/configUtils')); var loadOptions = require(path.join(global.pathToApp,'core/loadOptions')); global.app.use('/api/options', function(req, res){ var contextOptions = loadOptions(); var ref = req.headers.referer || ''; if (ref) { contextOptions = configUtils.getContextOptions(ref, contextOptions); } // Legacy options root structure var assetsOptions = contextOptions.assets; // TODO: https://github.com/sourcejs/Source/issues/142 assetsOptions.plugins = contextOptions.plugins; assetsOptions.rendering = contextOptions.rendering; assetsOptions.specInfo = contextOptions.specInfo; res.jsonp(assetsOptions); }); ================================================ FILE: core/auth.js ================================================ var everyauth = require('everyauth'); var fs = require('fs'); var ejs = require('./ejsWithHelpers.js'); var path = require('path'); module.exports = function(app) { "use strict"; app.states = app.states || {}; app.states.users = app.states.users || {}; var currentUserId = ""; // users data processing /** * @method getUser - user getter * * @param {String} id - github user id * * @returns {Object} - user - github user entity or empty object, if user is undefined */ var getUser = function(id) { return app.states.users[id] || {}; }; /** * @method setUser - user setter * * @param {Object} user - github user entity * * @param {String} user.id - required user field, which is used as user templral storage key. * * @returns {Object} user - returns user parameter */ var setUser = function(user) { if (typeof user !== "object" || !user.id) return; app.states.users[user.id] = user; return user; }; everyauth.everymodule.findUserById(function(id, callback) { callback(null, getUser(id)); }); // TODO: separated id & secret for dev mode everyauth.github .appId(global.opts.github.appId) .appSecret(global.opts.github.appSecret) .findOrCreateUser(function(sess, accessToken, accessTokenExtra, ghUser) { setUser(ghUser); currentUserId = ghUser.id; return ghUser; }) .redirectPath('/auth/done'); everyauth.everymodule.handleLogout( function (req, res) { delete req.session.authCache; req.logout(); this.redirect(res, this.logoutRedirectPath()); }); // application routes var authTemplate = fs.readFileSync(path.join(global.pathToApp, '/core/views/auth-done.ejs'), "utf8"); app.get('/auth/stub', function (req, res) { res.send(ejs.render(authTemplate, { user: JSON.stringify({}) })); }); app.get('/auth/done', function (req, res) { req.session.authCache = req.session.auth; res.send(ejs.render(authTemplate, { user: JSON.stringify(getUser(currentUserId)) })); }); return { getUser: getUser, setUser: setUser, everyauth: everyauth }; }; ================================================ FILE: core/ejsWithHelpers.js ================================================ 'use strict'; var ejs = require('ejs'); var fs = require('fs'); var glob = require('glob'); var path = require('path'); var _ = require('lodash'); var processMd = require(path.join(global.pathToApp,'core/lib/processMd')); var originalRenderer = ejs.render; var EJS_OPTS = ['cache', 'filename', 'sandbox', 'delimiter', 'scope', 'context', 'debug', 'compileDebug', 'client', '_with', 'rmWhitespace' ]; /** * Copy properties in data object that are recognized as options to an * options object. * * This is used for compatibility with earlier versions of EJS and Express.js. * * @memberof module:ejs-internal * @param {Object} data data object * @param {Options} opts options object * @static */ var cpOptsInData = function (data, opts) { EJS_OPTS.forEach(function (p) { if (typeof data[p] !== 'undefined') { opts[p] = data[p]; } }); }; var readFile = function (filePath, options) { if (options.sandbox && path.relative(options.sandbox, filePath).substring(0, 2) === '..') { throw new Error('reading files beyond sandbox is restricted, limit set to ' + options.sandbox); } return fs.readFileSync(filePath, 'utf-8'); }; var includeMD = function(data, options){ return function(mdPath){ if (!mdPath) return ''; var origionalFilename = options.filename; if (!origionalFilename) throw new Error('`includeMD` requires the \'filename\' option.'); var fileToInclude = path.extname(mdPath) === '.md' ? mdPath : mdPath + '.md'; var filePath = path.join(path.dirname(origionalFilename), fileToInclude); var fileContents = readFile(filePath, options); options.filename = filePath; var processedContents = ejs.render(fileContents, data, options); var html = processMd(processedContents); // Reset filename options on return options.filename = origionalFilename; return ejs.render(html, data, options); }; }; var includeFiles = function(data, options){ return function(pattern){ if (!pattern) return ''; var filename = options.filename; var output = ''; if (!filename) throw new Error('`includeFiles` requires the \'filename\' option.'); var filesToInclude = glob.sync(pattern, { cwd: path.dirname(filename), root: global.pathToApp, realpath: true }); filesToInclude.forEach(function(filePath){ _.assign(options, { filename: filePath }); output += ejs.render(readFile(filePath, options), data, options); }); // Reset filename options on return _.assign(options, { filename: filename }); return output; }; }; ejs.render = function(template, data, options){ data = data || {}; options = options || {}; // No options object -- if there are optiony names // in the data, copy them to options if (arguments.length === 2) { cpOptsInData(data, options); } _.assign(data, { includeMD: includeMD(data, options), includeFiles: includeFiles(data, options) }); if (global.opts.core.sandboxIncludes) { _.assign(options, { sandbox: global.pathToApp }); } return originalRenderer(template, data, options); }; // Export modified EJS module.exports = ejs; ================================================ FILE: core/file-tree/index.js ================================================ 'use strict'; var fs = require('fs-extra'); var extend = require('extend'); var deepExtend = require('deep-extend'); var path = require('path'); var extendTillSpec = require(path.join(global.pathToApp,'core/lib/extendTillSpec')); var unflatten = require(path.join(global.pathToApp,'core/unflat')); var specUtils = require(path.join(global.pathToApp,'core/lib/specUtils')); var utils = require(path.join(global.pathToApp,'core/lib/utils')); var coreOpts = global.opts.core; var prettyHrtime = require('pretty-hrtime'); var busy = false; var config = { includedDirs: coreOpts.common.includedDirs, excludedDirs: [], // TODO: merge with `excludedDirs` in next major release. excludedDirsGlobal: [], cron: false, cronProd: true, cronRepeatTime: 60000, outputFile: path.join(global.pathToApp, 'core/api/data/pages-tree.json'), specsRoot: path.join(global.pathToApp, coreOpts.common.pathToUser).replace(/\\/g, '/'), busyTimeout: 300, thumbnail: 'thumbnail.png' }; // Overwriting base options utils.extendOptions(config, coreOpts.fileTree); var normalizedPathToApp = global.pathToApp.replace(/\\/g, '/'); var prepareExcludesRegex = function(){ var dirsForRegExp = ''; var i = 1; config.excludedDirs.forEach(function (exlDir) { if (i < config.excludedDirs.length) { dirsForRegExp = dirsForRegExp + '^' + config.specsRoot + '\/' + exlDir + '|'; } else { dirsForRegExp = dirsForRegExp + '^' + config.specsRoot + '\/' + exlDir; } i++; }); return new RegExp(dirsForRegExp); }; /** * Prepare relative path for web usage (like `/docs/spec`, `/specs/btn`) out of absolute path * * @param {String} path - absolute path to Spec directory or Spec file * * @returns {String} Return Spec file meta info (used in file-tree.json and in other places) */ var getRelativeSpecPath = module.exports.getRelativeSpecPath = function(absolutePath){ var relativeSpecPath; if (absolutePath.lastIndexOf(config.specsRoot, 0) === 0) { // If starts with root (specs) // Cleaning path to specs root folder relativeSpecPath = absolutePath.replace(config.specsRoot, ''); } else { // Cleaning path for included folders relativeSpecPath = absolutePath.replace(normalizedPathToApp, ''); } return relativeSpecPath; }; var getSpecLocation = function(specDirOrPath){ // Normalize windows URL and remove trailing slash var _specDirOrPath = specDirOrPath.replace(/\\/g, '/').replace(/\/$/, ''); var isSpecPath = path.extname(_specDirOrPath) !== ''; var specDir; var specPath; var relativeSpecPath = getRelativeSpecPath(_specDirOrPath); if (isSpecPath) { relativeSpecPath = path.dirname(relativeSpecPath); } // Try to get specPath if (isSpecPath) { specDir = path.dirname(_specDirOrPath); specPath = _specDirOrPath; } else { specDir = _specDirOrPath; specPath = specUtils.getSpecFromDir(_specDirOrPath); } return { relativeSpecPath: relativeSpecPath, isSpecPath: isSpecPath, specDir: specDir, specPath: specPath }; }; /** * Get spec thumbnail image path * * @param {String} specDir - absolute path to spec dir * @param {String} [customThumbnailPath] - override default relative thumbnail path * * @returns {String} Return web url to spec thumbnail or undefined */ var getThumbnailPath = module.exports.getThumbnailPath = function(specDir, customThumbnailPath){ if (!specDir) return; var thumbnail; var thumbnailPathFromSpecDir = customThumbnailPath || config.thumbnail; var absoluteThumbNailPath = path.join(specDir, thumbnailPathFromSpecDir).replace(/\\/g, '/'); if (fs.existsSync(absoluteThumbNailPath)) { thumbnail = path.join(getRelativeSpecPath(specDir), thumbnailPathFromSpecDir); } return thumbnail; }; /** * Get contents of spec info file * * @param {String} specDir - absolute path to spec dir * * @returns {Object} Return spec info file object */ var getSpecInfoFile = module.exports.getSpecInfoFile = function(specDir){ var infoFileName = coreOpts.common.infoFile; var infoJsonPath = path.join(specDir, infoFileName); var infoContents = {}; if (!fs.existsSync(infoJsonPath)) return infoContents; try { infoContents = JSON.parse(fs.readFileSync(infoJsonPath, 'utf8')); } catch (e) { global.console.warn('Error reading ' + infoFileName + ': ' + infoJsonPath); infoContents = { error: 'Cannot parse the file', path: infoJsonPath }; } return infoContents; }; /** * Prepares Spec meta object from specs directory or Spec file path * * @param {String} specDirOrPath - path to Spec directory or Spec file * * @returns {Object} Return Spec file meta info (used in file-tree.json and in other places) */ var getSpecMeta = module.exports.getSpecMeta = function(specDirOrPath){ var page = {}; // Check if arg is provided and path exists if ( !(specDirOrPath && fs.existsSync(specDirOrPath))) return page; var specLocation = getSpecLocation(specDirOrPath); var specPath = specLocation.specPath; var specDir = specLocation.specDir; var relativeSpecPath = specLocation.relativeSpecPath; // Remove first slash for ID page.id = relativeSpecPath.substring(1); page.url = relativeSpecPath; // If we have Spec, get additional meta if (specPath) { var fileStats = fs.statSync(specPath); var targetFile = path.basename(specPath); var d = new Date(fileStats.mtime); page.lastmod = [d.getDate(), d.getMonth() + 1, d.getFullYear()].join('.') || ''; page.lastmodSec = Date.parse(fileStats.mtime) || ''; page.fileName = targetFile || ''; } var specInfo = getSpecInfoFile(specDir); page.thumbnail = getThumbnailPath(specDir, specInfo.thumbnailPath) || false; // Apply info file contents on top deepExtend(page, specInfo); return page; }; var fileTree = function (workingDir) { var processingDir = workingDir; var outputJSON = {}; // Allow to run app without existing specsRoot if (!fs.existsSync(workingDir) && workingDir === config.specsRoot) { global.log.warn('Running SourceJS without user dir. This set-up should be used only for running tests.'); processingDir = global.pathToApp; } var dirContent = fs.readdirSync(processingDir); var excludes = prepareExcludesRegex(); // Adding paths to files in array for (var i = 0; dirContent.length > i; i++) { dirContent[i] = path.join(processingDir, dirContent[i].replace(/\\/g, '/')); } //on first call we add includedDirs if (processingDir === config.specsRoot) { config.includedDirs.map(function (includedDir) { dirContent.push(path.join(normalizedPathToApp, includedDir)); }); } dirContent.forEach(function(pathToFile) { // Path is excluded if (excludes.test(processingDir)) return; var infoFileName = coreOpts.common.infoFile; var targetFile = path.basename(pathToFile); // Normalizing path for windows pathToFile = path.normalize(pathToFile).replace(/\\/g, '/'); var fileStats = fs.statSync(pathToFile); if (fileStats.isDirectory()) { if (config.excludedDirsGlobal.indexOf(targetFile) > -1) return; // Going deeper var childObj = fileTree(pathToFile); if (Object.getOwnPropertyNames(childObj).length !== 0) { outputJSON[targetFile] = extend(outputJSON[targetFile], childObj); } } else if (targetFile.toLowerCase() === infoFileName.toLowerCase()) { outputJSON['specFile'] = getSpecMeta(processingDir); } }); return outputJSON; }; // function for write file tree json var writeDataFile = function (data, callback) { var outputFile = config.outputFile; callback = typeof callback === 'function' ? callback : function(){}; fs.outputFile(outputFile, JSON.stringify(data, null, 4), function (err) { if (err) { global.console.warn('Error writing file tree: ', err); callback(err); return; } global.log.trace('Pages tree JSON saved to ' + outputFile); callback(); }); }; // function for updating file tree var updateFileTree = module.exports.updateFileTree = function (data, unflattenData, callback) { if (busy) { setTimeout(function(){ updateFileTree(data, unflattenData, callback); }, config.busyTimeout); } else { busy = true; var prevData = {}; var dataStoragePath = path.join(global.pathToApp, coreOpts.api.specsData); callback = typeof callback === 'function' ? callback : function(){}; if (unflattenData) { data = unflatten(data, { delimiter: '/', overwrite: 'root' }); } try { prevData = fs.readJsonFileSync(dataStoragePath); } catch (e) { global.log.trace('Reading initial data error: ', e); global.log.debug('Extending from empty object, as we do not have initial data'); } var dataToWrite = extendTillSpec(prevData, data); writeDataFile(dataToWrite, function(){ callback(); setTimeout(function(){ busy = false; }, config.busyTimeout); }); } }; // function for deleting object from file tree var deleteFromFileTree = module.exports.deleteFromFileTree = function (specID) { if (busy) { setTimeout(function(){ deleteFromFileTree(specID); }, config.busyTimeout); } else { fs.readJSON(config.outputFile, function (err, data) { if (err) return; busy = true; var pathSplit = specID.split('/'); var processPath = function (pathArr, obj) { var pathArrQueue = pathArr.slice(0); // Arr copy var currentItem = pathArrQueue.shift(); if (currentItem !== '' && obj[currentItem]) { if (pathArrQueue.length === 0) { delete obj[currentItem]; } if (pathArrQueue.length !== 0 && obj[currentItem].toString() === '[object Object]') { obj[currentItem] = processPath(pathArrQueue, obj[currentItem]); } } return obj; }; var processedData = processPath(pathSplit, data); writeDataFile(processedData, function () { global.log.trace('Deleted object from file tree: ', specID); setTimeout(function(){ busy = false; }, config.busyTimeout); }); }); } }; // function for update file tree json var scan = module.exports.scan = function (callback) { if (busy) { setTimeout(function(){ scan(callback); }, config.busyTimeout); } else { var start = process.hrtime(); callback = typeof callback === 'function' ? callback : function () {}; writeDataFile(fileTree(config.specsRoot), function(){ callback(); var end = process.hrtime(start); global.log.debug('Full file-tree scan took: ', prettyHrtime(end)); setTimeout(function(){ busy = false; }, config.busyTimeout); }); } }; // Running writeDataFile by cron if (config.cron || (global.MODE === 'production' && config.cronProd)) { setInterval(function () { scan(); }, config.cronRepeatTime); } ================================================ FILE: core/headerFooter.js ================================================ 'use strict'; var fs = require('fs'); var path = require('path'); exports.getHeaderAndFooter = function () { var defaultTemplatePath = path.join(global.pathToApp, 'assets/templates'); var userTemplatePath = path.join(global.app.get('user'), 'assets/templates'); var headerFile = 'header.inc.html'; var footerFile = 'footer.inc.html'; var output = {}; var userHeaderTplPath = path.join(userTemplatePath, headerFile); output.headerPath = fs.existsSync(userHeaderTplPath) ? userHeaderTplPath : path.join(defaultTemplatePath, headerFile); var userFooterTplPath = path.join(userTemplatePath, footerFile); output.footerPath = fs.existsSync(userFooterTplPath) ? userFooterTplPath : path.join(defaultTemplatePath, footerFile); output.header = fs.readFileSync(output.headerPath, 'utf-8'); output.footer = fs.readFileSync(output.footerPath, 'utf-8'); return output; }; ================================================ FILE: core/html-tree/html-parser/index.js ================================================ 'use strict'; var path = require('path'); var async = require('async'); var ParseData = require(path.join(global.pathToApp,'core/lib/parseData')); var phantom = require('phantomjs'); var unflatten = require(path.join(global.pathToApp,'core/unflat')); var childProcess = require('child_process'); var htmlTree = require(path.join(global.pathToApp,'core/html-tree')); var utils = require(path.join(global.pathToApp,'core/lib/utils')); var processFlagNotExec = true; var config = { enabled: true, // Run HTML parser on app start onStart: false, cron: false, cronProd: true, cronRepeatTime: 600000, // PhantomJS retry limit errorLimit: 2, asyncPhantomCallLimit: 5, specsFilter: { filterOut: { cats: ['docs'], tags: ['parse-problems'] } }, // Path to HTML data otput pathToSpecs: path.join(global.pathToApp, global.opts.core.api.specsData) }; // Overwriting base options if (global.opts.core.parseHTML) utils.extendOptions(config, global.opts.core.parseHTML); // Legacy support if (global.opts.plugins && global.opts.plugins.htmlParser) utils.extendOptions(config, global.opts.plugins.htmlParser); /** * Get list of specs for parsing with PhantomJS * * @returns {Array} Returns array with spec URLs */ var getSpecsList = function() { var parseSpecs = new ParseData({ scope: 'specs', path: require.resolve(config.pathToSpecs) }); var specs = parseSpecs.getFilteredData(config.specsFilter, true); // Preparing data for specs iteration specs = specs.map(function(item){ return item.url.substring(1); }); return specs; }; /** * PhantomJS async runner, calls writeDataFile on finish * * @param {Array} specs - array with URL list, that will be passed to PhantomJS * * @param {Function} [callback] - callback function * @param {Object} callback.err - Error object * @param {Object} callback.outputData - Passes output data to callback */ var processSpecs = module.exports.processSpecs = function(specs, callback){ callback = typeof callback === 'function' ? callback : function(){}; if (!config.enabled) { callback('HTML parser disabled.'); return; } if (processFlagNotExec) { global.log.info('HTML API update started'); var _specs = specs || getSpecsList(); var specsLeft = _specs.slice(0); var PhantomPath = phantom.path; var outputHTML = {}; var errorCounter = {}; var specLength = _specs.length; var doneCounter = 0; var phExecCommand = PhantomPath + " " + path.join(global.pathToApp, 'core/html-tree/html-parser/phantomRunner.js'); processFlagNotExec = false; global.log.trace('Processing ' + specLength + ' specs.'); async.mapLimit(_specs, config.asyncPhantomCallLimit, function (spec, next) { var n = _specs.indexOf(spec) + 1; global.log.trace('Starts...' + n, spec); childProcess.exec(phExecCommand + " " + spec + " " + global.opts.core.server.port, function (error, stdout, stderr) { handler(error, stdout, stderr, spec); next(); }); }); var handler = function(error, stdout, stderr, spec) { if (error) { if (typeof errorCounter[spec] !== 'number') { errorCounter[spec] = 0; } errorCounter[spec]++; // If limit is not reached, try again if (errorCounter[spec] <= config.errorLimit) { global.log.debug('Rerun', spec); childProcess.exec(phExecCommand + " " + spec, function (error, stdout, stderr) { handler(error, stdout, stderr, spec, writeCallback); }); return; } global.log.error('Exec error on spec ' + spec + ': '+ error); global.log.debug('Error info: ', JSON.stringify({ spec: spec, error: error, stdount: stdout, stderr: stderr })); } else { var parsedStdout = []; try { parsedStdout = JSON.parse(stdout); } catch(e) { global.log.debug('HTML Parser stdout parse error: ', e, stdout); global.log.debug('Error from Phantom parser: ', stdout); parsedStdout = { message: "Stdout parse error" }; } global.log.debug('Spec done: ', JSON.stringify({ spec: spec, error: error, stderr: stderr })); // Writing contents to common obj outputHTML[spec+'/specFile/contents'] = parsedStdout.contents; outputHTML[spec+'/specFile/headResources'] = parsedStdout.headResources; outputHTML[spec+'/specFile/bodyResources'] = parsedStdout.bodyResources; } global.log.debug((doneCounter/specLength*100).toFixed(2),'%...Done', spec); // Logging specs queen specsLeft.splice(specsLeft.indexOf(spec), 1); if (specsLeft.length < 5 && specsLeft.length !== 0) { global.log.trace('Specs queen', specsLeft); } doneCounter++; // We handled all requested specs if (doneCounter === specLength) { var outputData = unflatten(outputHTML, { delimiter: '/', overwrite: 'root' }); // Callback is passed to writeDataFile var writeCallback = function() { global.log.info('HTML API successfully updated'); processFlagNotExec = true; callback(null, outputData); }; htmlTree.writeDataFile(outputData, true, false, writeCallback); } }; } }; if (config.enabled) { // Running processSpecs by cron if (config.cron || (global.MODE === 'production' && config.cronProd)) { setInterval(function () { processSpecs(); }, config.cronRepeatTime); } if (config.onStart) { setTimeout(processSpecs, 100); } } ================================================ FILE: core/html-tree/html-parser/phantomRunner.js ================================================ /* * * This script is executed in separate process from main app and runs in PhantomJS context * * */ 'use strict'; /* global phantom: true */ var page = require('webpage').create(); var system = require('system'); // arguments from node query var url = system.args[1]; var port = system.args[2]; page.onResourceReceived = function(response) { if (response.id === 1 && response.status === 404 || response.status === 500) { console.log(JSON.stringify({ "error": "Network error status "+ response.status, "url": url }) ); phantom.exit(); } }; page.onConsoleMessage = function(msg) { //console.log('-- webkit console: ' + msg); }; page.open('http://127.0.0.1:' + port + '/' + url, function (status) { if (status !== 'success') { console.log(JSON.stringify({ "error": "Error loading page.", "url": url }) ); phantom.exit(); } setTimeout(function () { console.log(JSON.stringify([{ "error": "Too long execution time.", "url": url }]) ); phantom.exit(); }, 5000); }); page.onCallback = function (data) { if (data.message) { var code = page.evaluate(function (url) { var output = {}; $.ajax({ url: '/source/assets/js/modules/sectionsParser.js', dataType: "script", async: false, success: function(){ var parser = new SourceGetSections(); output = parser.getSpecFull(); } }); return output; }, url); // TODO: make reponse in {{ ... }} to parse only relevant part // Returns stdout, that is then parsed from main app console.log(JSON.stringify(code)); phantom.exit(); } else { console.log("No callback received", JSON.stringify({ "url": url }) ); phantom.exit(); } }; page.onError = function(msg, trace) { var log = { "error": "Error onpage", "message": msg, "file": trace[0], "line": trace[0].line, "function": trace[0].function }; console.log('Phantom-runner error: ', JSON.stringify(log, null, 4)); }; ================================================ FILE: core/html-tree/index.js ================================================ 'use strict'; var path = require('path'); var fs = require('fs-extra'); var extendTillSpec = require(path.join(global.pathToApp,'core/lib/extendTillSpec')); var logger = require(path.join(global.pathToApp,'core/logger')); var utils = require(path.join(global.pathToApp,'core/lib/utils')); var flattenTillSpec = require(path.join(global.pathToApp,'core/lib/flattenTillSpec')); var config = {}; // Overwriting base options if (global.opts.core.htmlTree) utils.extendOptions(config, global.opts.core.htmlTree); // Custom API logging config var apiLog = (function(){ logger.prepareLogDir('log/api'); logger.addAppenders([ { "type": "clustered", "appenders": [ { "type": "file", "filename": path.join(global.pathToApp, 'log/api/api.log') }, { "type": "logLevelFilter", "level": "ERROR", "appender": { "type": "file", "filename": path.join(global.pathToApp, 'log/api/errors.log') } }, { "type": "logLevelFilter", "level": "DEBUG", "appender": { "type": "file", "filename": path.join(global.pathToApp, 'log/api/debug.log') } } ], category: 'api' } ]); return logger.log4js.getLogger('api'); })(); /** * Remove all objects from data, that has lower priority (doesn't have forcedSave flag) * * @param {Object} prevData - data to check more priority specs from * @param {Object} data - data that will be merged onto prevData and will be processed with this func * * @returns {Object} Returns processed data, with removed low-priority specs */ var excludeLowOverridings = function(prevData, data) { var checkObj = flattenTillSpec(prevData); var processData = function(obj, currentNesting){ Object.keys(obj).forEach(function(key){ var nesting = currentNesting ? currentNesting + '/' + key : key; if (obj[key].toString() === '[object Object]') { if (!obj[key].specFile) { // Go deeper obj[key] = processData(obj[key], nesting); } else { // Check prevData on force flag var checkHigherPrioritySpec = !!(checkObj[nesting] && checkObj[nesting].forcedSave); if (checkHigherPrioritySpec && !obj[key].specFile.forcedSave) { // Delete low priority override delete obj[key]; } } } }); return obj; }; return processData(data); }; /** * Write gathered HTML data to file system * * @param {Object} data - data object with specs list, that will be stringified and written to FS * @param {Boolean} [extend] - set true, to merge incoming data object with last written data * @param {String} [dataPath] - custom data storage path * * @param {Function} [callback] - callback function on file write * @param {Object} callback.err - Passes error if it exists * @param {Object} callback.outputData - Passes output data to callback */ var writeDataFile = module.exports.writeDataFile = function(data, extend, dataPath, callback) { if (data) { var dataStoragePath = dataPath || path.join(global.pathToApp, global.opts.core.api.htmlData); var JSONformat = null; if (global.MODE === 'development') JSONformat = 4; if (extend) { //TODO: add queen, for waiting till previous processing stops working before we update with extend var prevData = {}; try { prevData = fs.readJsonFileSync(dataStoragePath); } catch (e) { apiLog.trace('Reading initial data error: ', e); apiLog.debug('Extending from empty object, as we do not have initial data'); } // Exclude from data all low-priority overridings data = excludeLowOverridings(prevData, data); // Extend final data data = extendTillSpec(prevData, data); } // Preparing path for data write try { fs.mkdirpSync(path.dirname(dataStoragePath)); } catch (e) { if (e.code !== 'EEXIST') { apiLog.warn("Could not set up HTML data directory, error: ", e); if (typeof callback === 'function') callback('ERROR: error creating data directory', null); } } fs.writeFile(dataStoragePath, JSON.stringify(data, null, JSONformat), function (err) { if (err) { var message = 'ERROR: updated file write error'; apiLog.warn('HTML data write fail, write file error', err); if (global.MODE === 'development') message = message + ': ' + err; if (typeof callback === 'function') callback(message, null); } else { apiLog.debug('parseHTML data been written.'); // After all specs were both written in file and saved in memory. if (typeof callback === 'function') callback(null, data); } }); } else { apiLog.warn('HTML data write fail, no data provided for writeDataFile'); if (typeof callback === 'function') callback('ERROR: no data provided', null); } }; /** * Delete object from HTML data storage * * @param {String} dataPath - path to data object * @param {String} removeID - object ID for removal * @param {Function} [callback] - callback function on file write */ module.exports.deleteFromDataFile = function(dataPath, removeID, callback) { if (removeID) { var data; var pathSplit = removeID.split('/'); try { data = fs.readJsonFileSync(dataPath); } catch (e) {} if (data) { var processPath = function(pathArr, obj){ var pathArrQueue = pathArr.slice(0); // Arr copy var currentItem = pathArrQueue.shift(); if (currentItem !== '' && obj[currentItem]) { if (pathArrQueue.length === 0) { delete obj[currentItem]; } if (pathArrQueue.length !== 0 && obj[currentItem].toString() === '[object Object]') { obj[currentItem] = processPath(pathArrQueue, obj[currentItem]); } } return obj; }; var processedData = processPath(pathSplit, data); writeDataFile(processedData, false, dataPath, function(err, finalData){ if (err || !finalData) { if (typeof callback === 'function') callback(err, null); } else { if (typeof callback === 'function') callback(null, finalData); } }); } else { var errorMsg = 'No initial HTML data to delete from'; apiLog.warn(errorMsg); if (typeof callback === 'function') callback(errorMsg, null); } } else { if (typeof callback === 'function') callback('No ID provided', null); } }; ================================================ FILE: core/lib/configUtils.js ================================================ 'use strict'; var fs = require('fs-extra'); var path = require('path'); var finder = require('fs-finder'); var nodeUtils = require('util'); var url = require('url'); var utils = require(path.join(global.pathToApp, 'core/lib/utils')); var pathResolver = require(path.join(global.pathToApp, 'core/lib/pathResolver')); var specUtils = require(path.join(global.pathToApp, 'core/lib/specUtils')); var coreOptionsInContextWarnOnce = []; /** * Searches sourcejs-plugins in node_modules of specified folder * * @param {String} pathToUser - path to user folder, where to check node_modules * * @returns {Object} Returns configuration object with listed sourcejs-plugins */ module.exports.prepareClientNpmPlugins = function(pathToUser) { var pathToModules = path.join(pathToUser, 'node_modules'); var clientNpmPlugins = {}; clientNpmPlugins.assets = {}; clientNpmPlugins.assets.npmPluginsEnabled = {}; if (fs.existsSync(pathToModules)) { var allModules = fs.readdirSync(pathToModules); allModules.map(function (module) { // Check if module has right namespace AND assets/index.js file if (module.lastIndexOf('sourcejs-', 0) === 0 && fs.existsSync(path.join(pathToModules, module, 'assets/index.js'))) { clientNpmPlugins.assets.npmPluginsEnabled[module] = true; } }); } return clientNpmPlugins; }; /** * Search up the tree for context options paths * * @param {String} startPath - path to dir from where to start searching * * @returns {Array} Returns list of paths with found options files */ var getContextOptionsList = module.exports.getContextOptionsList = function(startPath) { var checkPath = fs.existsSync(startPath); if (!checkPath) return []; var searchStopPath = global.app.get('user'); var fileToFind = path.sep + global.opts.core.common.contextOptionsFile; // Skip if start path is behind stop path if ((new RegExp(/^..\//)).test(path.relative(searchStopPath, startPath).replace(/\\/g, '/'))) return []; return finder.in(startPath).lookUp(searchStopPath).findFiles(fileToFind); }; /** * Process options view path * * @param {Object} options - options object to process * @param {String} [context] - path to context folder for resolving $(context) * * @returns {Object} Returns processed options object or undefined */ var processOptionsViewPaths = module.exports.processOptionsViewPaths = function(options, context) { if (!options) return; var updateArr = function(optItem){ return optItem.map(function (item) { return pathResolver.resolve(item, context); }); }; for (var opt in options.rendering.views) { if (options.rendering.views.hasOwnProperty(opt)) { var optItem = options.rendering.views[opt]; if (nodeUtils.isArray(optItem)) { options.rendering.views[opt] = updateArr(optItem); } } } return options; }; /** * Process options object * * @param {String} optionsPath - path to options file * @param {Object} [optionsObj] - options file content * * @returns {Object} Returns processed options object */ var processOptions = module.exports.processOptions = function(optionsPath, optionsObj) { var optionsDir = path.dirname(optionsPath); var options = optionsObj || utils.requireUncached(optionsPath); if (options.rendering && options.rendering.views) processOptionsViewPaths(options, optionsDir); return options; }; var extendContextOptions = module.exports.extendContextOptions = function(defaultOptions, newOptionsPath, newOptionsObj) { var output = defaultOptions || {}; var contextOptionsItem = processOptions(newOptionsPath, newOptionsObj); if (contextOptionsItem.core && coreOptionsInContextWarnOnce.indexOf(newOptionsPath) === -1) { global.log.warn('Core options could not be overridden from context options, check ' + newOptionsPath); coreOptionsInContextWarnOnce.push(newOptionsPath); } // Override default options with context options items if (output.assets) utils.extendOptions(output.assets, contextOptionsItem.assets); if (output.rendering) utils.extendOptions(output.rendering, contextOptionsItem.rendering); if (output.plugins) utils.extendOptions(output.plugins, contextOptionsItem.plugins); return output; }; /** * Get merged options object, with merged context level options * * @param {String} startPath - path from where to start searching for context options (ends at user path) * @param {Object} [defaultOptions] - SourceJS options object (global.opts) * * @returns {Object} Returns a merged options object */ var getMergedOptions = module.exports.getMergedOptions = function(startPath, defaultOptions) { var _defaultOptions = defaultOptions || {}; var output = utils.extendOptions({}, _defaultOptions); var optionsArr = getContextOptionsList(startPath); // Normalize windows paths optionsArr = optionsArr.map(function(item){ return item.replace(/\\/g, '/'); }); optionsArr = optionsArr.sort(function (a, b) { var al = a.split('/').length; var bl = b.split('/').length; if (al > bl) return 1; if (al < bl) return -1; return 0; }); optionsArr.forEach(function(newOptionsPath){ extendContextOptions(output, newOptionsPath); }); return output; }; /** * Get context options using ref URL * * @param {String} refUrl - referer URL * @param {Object} [defaultOpts] - default options object to merge in * * @returns {Object} Returns a context options object */ module.exports.getContextOptions = function(refUrl, defaultOpts) { var _defaultOpts = defaultOpts || global.opts; var contextOptionsEnabled = global.opts.core.common.contextOptions; var parsedRefUrl = url.parse(refUrl); var refUrlPath = parsedRefUrl.pathname; var specDir = specUtils.getFullPathToSpec(refUrlPath); var contextOptions = contextOptionsEnabled ? getMergedOptions(specDir, _defaultOpts) : _defaultOpts; var infoFilePath = path.join(specDir, global.opts.core.common.infoFile); var infoOptionsKey = global.opts.core.common.infoFileOptions; // Extent context options object with info.json contents contextOptions.specInfo = fs.readJsonFileSync(infoFilePath, {throws: false}); // Override local options if (contextOptionsEnabled && contextOptions.specInfo && contextOptions.specInfo[infoOptionsKey]) { extendContextOptions(contextOptions, infoFilePath, contextOptions.specInfo[infoOptionsKey]); } return contextOptions; }; ================================================ FILE: core/lib/createLink.js ================================================ // Modified lib from Bower 'use strict'; var fs = require('fs-extra'); var path = require('path'); var Q = require('q'); var isWin = process.platform === 'win32'; function createLink(src, dst, type) { var dstDir = path.dirname(dst); // Create directory return Q.nfcall(fs.mkdirp, dstDir) // Check if source exists .then(function () { return Q.nfcall(fs.stat, src) .fail(function (error) { if (error.code === 'ENOENT') { console.log('Failed to create link to ' + path.basename(src), 'ENOENT', { details: src + ' does not exist or points to a non-existent file' }); } throw error; }); }) // Create symlink .then(function (stat) { type = type || (stat.isDirectory() ? 'dir' : 'file'); return Q.nfcall(fs.symlink, src, dst, type) .fail(function (err) { if (!isWin || err.code !== 'EPERM') { throw err; } // Try with type "junction" on Windows // Junctions behave equally to true symlinks and can be created in // non elevated terminal (well, not always..) return Q.nfcall(fs.symlink, src, dst, 'junction') .fail(function (err) { console.log('Unable to create link to ' + path.basename(src), err.code, { details: err.message.trim() + '\n\nTry running this command in an elevated terminal (run as root/administrator).' }); }); }); }); } module.exports = createLink; ================================================ FILE: core/lib/extendTillSpec.js ================================================ 'use strict'; /** * Extend object till "specFile" key * * @param {Object} target - Target data that will be extended * @param {Object} source - Source data to extend with * * @returns {Object} Return extended data */ var extendTillSpec = module.exports = function(target, extender) { for (var key in extender) { if (extender.hasOwnProperty(key)) { if (!(key in extender)) continue; var src = target[key]; var val = extender[key]; if (val === target) continue; if (typeof val !== 'object' || key === "specFile" || val === null) { target[key] = val; continue; } if (typeof src !== 'object' || src === null) { target[key] = extendTillSpec({}, val); continue; } target[key] = extendTillSpec(src, val); } } return target; }; ================================================ FILE: core/lib/flattenTillSpec.js ================================================ 'use strict'; /** * Flatten given data * * @param {Object} [data] - Data object wil all specs/html * * @returns {Object} Return flattened data */ module.exports = function(data) { var delimiter = '/'; var output = {}; var _data = data; var step = function(object, prev) { Object.keys(object).forEach(function (key) { var value = object[key]; var isSpecFile = key === 'specFile'; var keyAppend = isSpecFile ? '' : delimiter + key; var newKey = prev ? prev + keyAppend : key; if (typeof value === 'object' && !isSpecFile) { return step(value, newKey); } output[newKey] = value; }); }; step(_data); return output; }; ================================================ FILE: core/lib/parseData.js ================================================ 'use strict'; var fs = require('fs'); var util = require('util'); var path = require('path'); var flattenTillSpec = require(path.join(global.pathToApp, 'core/lib/flattenTillSpec')); /** * ParseData Constructor for working with Specs File tree and HTML tree * * @class ParseData * @constructor * @param {Object} config * @param {String} config.scope - data scope (specs/html) * @param {String} config.path - path do data */ function ParseData(config) { this.data = {}; this.scope = config.scope; this.dataPath = config.path; } /** * Update data and return data status * * @param {Boolean} [withCategories] - get categories info either * * @returns {Boolean} Return data status */ ParseData.prototype.updateData = function(withCategories) { try { this.data = JSON.parse(fs.readFileSync(this.dataPath, 'utf8')); } catch(e) { return false; } this.data = flattenTillSpec(this.data); if (this.scope === 'specs' && !withCategories) { this.removeCatalogueDescription(); } return true; }; /** * Removing catalogue description objects * * @param {Object} [data] - Data object wil all specs/html * * @returns {Object} Return data without catalogue description */ ParseData.prototype.removeCatalogueDescription = function(data) { var output = {}; var _data = data || this.data; Object.keys(_data).forEach(function (key) { var value = _data[key]; if (value['role'] === 'navigation') return; output[key] = value; }); this.data = output; return output; }; /** * Get all data * * @param {Boolean} [withCategories] - get categories info either * * @returns {Object} Return flat all data object with all items */ ParseData.prototype.getAll = function(withCategories) { return this.updateData(withCategories) ? this.data : undefined; }; /** * Get raw * * @returns {Object} Return nested, raw data object with all items */ ParseData.prototype.getRaw = function() { try { return JSON.parse(fs.readFileSync(this.dataPath, 'utf8')); } catch(e) { console.warn('Parse Data: Api data of ' + this.scope + ' does not exist, please update HTML API data.'); console.warn('Error: ', e); return undefined; } }; /** * Filter placeholder function * * @param {Array} filterArr - Array with filtering params * @param {Function} filterFunc - callback function * example: filterFunc(filterItem) — accepts data item key for param * * @returns {Boolean} Return boolean with filter result */ ParseData.prototype._filter = function(filterArr, filterFunc){ var passesFilter = true; if (util.isArray(filterArr)) { filterArr.map(function(filterItem) { if (!filterFunc(filterItem)) passesFilter = false; }); } return passesFilter; }; /** * Filtering by fields * * @param {Object} value - One item data, simple object without nesting * @param {Boolean} inOut - for filtering and filteringOut * @param {Array} filterArr - Array with filtering params * * @returns {Boolean} Return boolean with final filter result */ ParseData.prototype.filterFields = function(value, inOut, filterArr) { return this._filter(filterArr, function(filterItem) { return Boolean(value[filterItem]) === inOut; }); }; /** * Filtering by tags * * @param {Object} value - One item data, simple object without nesting * @param {Boolean} inOut - for filtering and filteringOut * @param {Array} filterArr - Array with filtering params * * @returns {Boolean} Return boolean with final filter result */ ParseData.prototype.filterTags = function(value, inOut, filterArr) { return this._filter(filterArr, function(filterItem) { if(!util.isArray(value.tag)) { return false === inOut; } else { return (value.tag.indexOf(filterItem) > -1) === inOut; } }); }; /** * Filtering by category * * @param {Object} value - One item data, simple object without nesting * @param {String} key - Current key of looped object * @param {Boolean} inOut - for filtering and filteringOut * @param {Array} filterArr - Array with filtering params * * @returns {Boolean} Return boolean with final filter result */ ParseData.prototype.filterCats = function(value, key, inOut, filterArr) { return this._filter(filterArr, function(filterItem) { return Boolean(key.lastIndexOf(filterItem, 0)) !== inOut; }); }; /** * Filters given data by provided conf * * @param {Object} filterConf - Filter configuration * * @param {Object} filterConf.filter - Check if exists * @param {Array} filterConf.filter.fields - Array with fields to filter * @param {Array} filterConf.filter.cats - Array with cats to filter * @param {Array} filterConf.filter.tags - Array with tags to filter * * @param {Object} filterConf.filterOut - Check if not exists * @param {Array} filterConf.filterOut.fields - Array with fields to filter * @param {Array} filterConf.filterOut.cats - Array with cats to filter * @param {Array} filterConf.filterOut.tags - Array with tags to filter * * @param {Boolean} [array] - Set true, if you want to get array as resposne * @param {Object} [data] - Data to filter * * @returns {Object} Returns object or array with filtered data or undefined */ ParseData.prototype.getFilteredData = function(filterConf, array, data) { var _this = this; var _data = {}; var dataExists = true; var output = {}; if (array) { output = []; } if (data) { _data = data; } else { dataExists = this.updateData(); _data = this.data; } if (dataExists) { Object.keys(_data).forEach(function (key) { var value = _data[key]; var filterObj = filterConf.filter; var filterOutObj = filterConf.filterOut; // Filtering categories out if (filterObj && filterObj.cats && !_this.filterCats(value, key, true, filterObj.cats)) return; if (filterOutObj && filterOutObj.cats && !_this.filterCats(value, key, false, filterOutObj.cats)) return; // Filtering by existing and not empty fields if (filterObj && filterObj.fields && !_this.filterFields(value, true, filterObj.fields)) return; if (filterOutObj && filterOutObj.fields && !_this.filterFields(value, false, filterOutObj.fields)) return; // Filtering by tags if (filterObj && filterObj.tags && !_this.filterTags(value, true, filterObj.tags)) return; if (filterOutObj && filterOutObj.tags && !_this.filterTags(value, false, filterOutObj.tags)) { if ( !(filterObj && filterObj.forceTags && _this.filterTags(value, true, filterObj.forceTags)) ) { return; } } if (array) { output.push(value); } else { output[key] = value; } }); return output; } else { return undefined; } }; /** * Get item by ID * * @param {String} id - Request some item by id (for example "base/btn") * * @returns {Object} Return single object by requested ID, null (no id found) or undefined (no data found) */ ParseData.prototype.getByID = function(id) { var dataExists = this.updateData(); var targetData = this.data[id]; if (!dataExists) return undefined; if (!targetData) return null; return targetData; }; /** * Flatten contents of Spec HTML object * * @param {Object} contents - Nested Spec HTML contents object * * @returns {Object} Return flat object with contents, grouped by section ID */ ParseData.prototype.flattenHTMLcontents = function(contents) { var flatList = {}; var parseContents = function(contents){ for (var i=0; contents.length > i ; i++) { var current = contents[i]; flatList[current.id] = current; if (current.nested.length > 0) { parseContents(current.nested); } } }; parseContents(contents); return flatList; }; /** * Get specific sections of defined Spec HTML * * @param {String} id - Request some item by id (for example "base/btn") and sections * @param {Array} sections - Array of sections to return * * @returns {Object} Return single object by requested ID, with specified sections HTML OR undefined */ ParseData.prototype.getBySection = function(id, sections) { // Sections are defined only in html data storage if (this.scope === 'html' && Array.isArray(sections) && sections.length > 0) { var specData = this.getByID(id); if (specData) { var specSections = this.flattenHTMLcontents(specData.contents); var pickedSections = []; sections.forEach(function(id){ var objectToAdd = specSections[id]; if (objectToAdd) pickedSections.push(objectToAdd); }); if (pickedSections.length !== 0) { specData.contents = pickedSections; return specData; } else { return undefined; } } else { return undefined; } } else { return undefined; } }; module.exports = ParseData; ================================================ FILE: core/lib/pathResolver.js ================================================ 'use strict'; var path = require('path'); var fs = require('fs'); var util = require('util'); /** * Replace stubs in path ($(sourcejs), $(user), $(context) etc) * * @param {String} pathToResolve - path to modify * @param {String} [context] - folder dir, to define $(context) resolver * * @returns {String} Return resolved path */ var resolve = module.exports.resolve = function(pathToResolve, context){ if (!pathToResolve) return; var map = { sourcejs: global.pathToApp, user: global.app.get('user'), context: context || global.app.get('user') }; var result = pathToResolve; global.log.trace('Resolve path', result); for (var tag in map) { if (map.hasOwnProperty(tag)) { var tagVal = map[tag]; var prepareRegExpString = '$('+tag+')'; prepareRegExpString = prepareRegExpString.replace(/[$-\/?[-^{|}]/g, '\\$&'); var re = new RegExp(prepareRegExpString, 'g'); result = result.replace(re, tagVal); } } global.log.trace('Resolve path returned', result); return result; }; /** * Check file or path existence following the array of options * * @param {Array} pathsArr - array of paths to check * @param {String} [filePath] - file path to append for each option * @param {String} [context] - folder dir, to define $(context) resolver * * @returns {String} Return first existing path or undefined */ module.exports.checkExistence = function(pathsArr, filePath, context){ if (!pathsArr && !util.isArray(pathsArr)) return; var fullPath; global.log.trace('Check existence', pathsArr, filePath); for(var i=0; i < pathsArr.length; i++) { var checkPath = filePath ? path.join(pathsArr[i], filePath) : pathsArr[i]; checkPath = resolve(checkPath, context); if (fs.existsSync(checkPath)) { fullPath = checkPath; break; } } global.log.trace('Check existence returned', fullPath); return fullPath; }; ================================================ FILE: core/lib/processMd.js ================================================ 'use strict'; var path = require('path'); var marked = require('marked'); var cheerio = require('cheerio'); var deepExtend = require('deep-extend'); var translit = require(path.join(global.pathToApp,'core/lib/translit')); var utils = require(path.join(global.pathToApp,'core/lib/utils')); var renderer = new marked.Renderer(); // Module configuration var globalConfig = global.opts.core && global.opts.core.processMd ? global.opts.core.processMd : {}; var config = { espaceCodeHTML: true, languageRenderers: { example: function (code) { return '
      ' + code + '
      '; } }, // Define marked module options marked: {} }; // Overwriting base options utils.extendOptions(config, globalConfig); // Processing with native markdown renderer renderer.code = function (code, language) { if (config.languageRenderers.hasOwnProperty(language)) { return config.languageRenderers[language](code); } else { if (config.espaceCodeHTML) code = code.replace(//g, ">"); if (language && language !== '') { return '' + code + ''; } else { return '
      ' + code + '
      '; } } }; renderer.heading = function (text, level) { var escapedText = translit(text); return '' + text + ''; }; // Extend re-defined renderer config.marked.renderer = deepExtend(renderer, config.marked.renderer); marked.setOptions(config.marked); module.exports = function (markdown, options) { var _options = options || {}; var $ = cheerio.load('
      ' + marked(markdown) + '
      '); var $content = $('#content').first(); if (_options.wrapDescription) { // Spec description var $startElement; var $H1 = $content.children('h1').first(); if ($H1.length > 0) { $startElement = $H1; } else { $content.prepend('
      '); $startElement = $content.children('#sourcejs-start-element').first(); } var $description = $startElement.nextUntil('h2'); $description.remove(); $startElement.after('
      ' + $description + '
      '); $content.children('#sourcejs-start-element').first().remove(); } // Spec sections $content.children('h2').each(function () { var $this = $(this); var $filteredElems = $(''); var $sectionElems = $this.nextUntil('h2'); var id = $this.attr('id'); $this.removeAttr('id'); // Adding additional check, since cheerio .nextUntil is not stable $sectionElems.each(function () { if (this.tagName === 'h2') return false; $filteredElems = $filteredElems.add(this); }); $filteredElems.remove(); $(this).replaceWith([ '
      ', $this + $filteredElems, '
      ' ].join('')); }); return $content.html(); }; ================================================ FILE: core/lib/specUtils.js ================================================ 'use strict'; var path = require('path'); var fs = require('fs-extra'); var parseData = require(path.join(global.pathToApp, 'core/lib/parseData')); /** * Parse clean path to spec from URL * * @param {String} urlPath - raw url ("/base/spec/index.src.html") * * @returns {Object} output * @returns {String} output.ext - file extension, if exists * @returns {String} output.pathToSpec - path to Spec */ var parseSpecUrlPath = module.exports.parseSpecUrlPath = function(urlPath){ // TODO: add any type of url parsing, including parameters var output = {}; var splitExt = urlPath.split('.'); var hasExt = splitExt.length > 1; if (hasExt) { output.ext = splitExt[1]; output.pathToSpec = path.dirname(urlPath); } else { output.pathToSpec = urlPath; } return output; }; /** * Parse spec ID from URL * * @param {String} urlToSpec - spec path or url ("/base/btn/") * * @returns {String} Return a parsed Spec ID */ var getSpecIDFromUrl = module.exports.getSpecIDFromUrl = function(urlToSpec){ urlToSpec = urlToSpec.replace(/^\//, '').replace(/\/+$/, ''); return urlToSpec; }; /** * Get information about defined specSpec * * @param {String} urlSpecPath - spec path from url * * @returns {Object} Return single info object of the spec */ module.exports.getSpecInfo = function(urlSpecPath) { var specsDataPath = path.join(global.pathToApp, global.opts.core.api.specsData); var parseSpecData = new parseData({ scope: 'specs', path: specsDataPath }); var specID = getSpecIDFromUrl(urlSpecPath); return parseSpecData.getByID(specID); }; /** * Get Spec name from defined directory * * @param {String} dirPath - Spec directory * @param {Array} [specFiles] - list of spec files to check * * @returns {String} Return first found Spec file path or undefined */ module.exports.getSpecFromDir = function(dirPath, specFiles) { var dirContent = fs.existsSync(dirPath) ? fs.readdirSync(dirPath) : undefined; if (!dirContent) return; var supportedSpecNames = specFiles || global.opts.rendering.specFiles; var specPath; for (var i=0; i < supportedSpecNames.length; i++) { var item = supportedSpecNames[i]; // Support folders inside names, e.g. 'docs/index.html' if (item.indexOf('/') !== -1) { var filename = path.join(dirPath, item); if (fs.existsSync(filename)) { specPath = filename; break; } } else { if (dirContent.indexOf(item) > -1) { specPath = path.join(dirPath, item); break; } } } return specPath; }; /** * Get absolute path to Spec dir * * @param {String} urlPath - relative URL (web) to spec * * @returns {String} Return absolute path to Spec dir */ module.exports.getFullPathToSpec = function(urlPath){ var pathToSpec = parseSpecUrlPath(urlPath).pathToSpec; var cleanPath = urlPath.replace(/\/+$/, '').replace(/\//, ''); var specPath = path.join(global.app.get('user'), pathToSpec).replace(/\\/g, '/'); // Including non-standard paths, outside default static route global.opts.core.common.includedDirs.forEach(function(item){ if (cleanPath.split('/')[0] === item) { specPath = specPath.replace('/' + global.opts.core.common.pathToUser + '/' + cleanPath, '/' + cleanPath); } }); // remove trailing slash return specPath.replace(/\/+$/, ''); }; ================================================ FILE: core/lib/translit.js ================================================ /* jshint -W089 */ /* global XRegExp */ var char_map = { /* jshint ignore:start */ // Latin 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U', 'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y', // Latin symbols '©': '(c)', // Greek 'Α': 'A', 'Β': 'B', 'Γ': 'G', 'Δ': 'D', 'Ε': 'E', 'Ζ': 'Z', 'Η': 'H', 'Θ': '8', 'Ι': 'I', 'Κ': 'K', 'Λ': 'L', 'Μ': 'M', 'Ν': 'N', 'Ξ': '3', 'Ο': 'O', 'Π': 'P', 'Ρ': 'R', 'Σ': 'S', 'Τ': 'T', 'Υ': 'Y', 'Φ': 'F', 'Χ': 'X', 'Ψ': 'PS', 'Ω': 'W', 'Ά': 'A', 'Έ': 'E', 'Ί': 'I', 'Ό': 'O', 'Ύ': 'Y', 'Ή': 'H', 'Ώ': 'W', 'Ϊ': 'I', 'Ϋ': 'Y', 'α': 'a', 'β': 'b', 'γ': 'g', 'δ': 'd', 'ε': 'e', 'ζ': 'z', 'η': 'h', 'θ': '8', 'ι': 'i', 'κ': 'k', 'λ': 'l', 'μ': 'm', 'ν': 'n', 'ξ': '3', 'ο': 'o', 'π': 'p', 'ρ': 'r', 'σ': 's', 'τ': 't', 'υ': 'y', 'φ': 'f', 'χ': 'x', 'ψ': 'ps', 'ω': 'w', 'ά': 'a', 'έ': 'e', 'ί': 'i', 'ό': 'o', 'ύ': 'y', 'ή': 'h', 'ώ': 'w', 'ς': 's', 'ϊ': 'i', 'ΰ': 'y', 'ϋ': 'y', 'ΐ': 'i', // Turkish 'Ş': 'S', 'İ': 'I', 'Ç': 'C', 'Ü': 'U', 'Ö': 'O', 'Ğ': 'G', 'ş': 's', 'ı': 'i', 'ç': 'c', 'ü': 'u', 'ö': 'o', 'ğ': 'g', // Russian 'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'Yo', 'Ж': 'Zh', 'З': 'Z', 'И': 'I', 'Й': 'J', 'К': 'K', 'Л': 'L', 'М': 'M', 'Н': 'N', 'О': 'O', 'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T', 'У': 'U', 'Ф': 'F', 'Х': 'H', 'Ц': 'C', 'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Sh', 'Ъ': '', 'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya', 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'j', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch', 'ш': 'sh', 'щ': 'sh', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya', // Ukrainian 'Є': 'Ye', 'І': 'I', 'Ї': 'Yi', 'Ґ': 'G', 'є': 'ye', 'і': 'i', 'ї': 'yi', 'ґ': 'g', // Czech 'Č': 'C', 'Ď': 'D', 'Ě': 'E', 'Ň': 'N', 'Ř': 'R', 'Š': 'S', 'Ť': 'T', 'Ů': 'U', 'Ž': 'Z', 'č': 'c', 'ď': 'd', 'ě': 'e', 'ň': 'n', 'ř': 'r', 'š': 's', 'ť': 't', 'ů': 'u', 'ž': 'z', // Polish 'Ą': 'A', 'Ć': 'C', 'Ę': 'e', 'Ł': 'L', 'Ń': 'N', 'Ó': 'o', 'Ś': 'S', 'Ź': 'Z', 'Ż': 'Z', 'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n', 'ó': 'o', 'ś': 's', 'ź': 'z', 'ż': 'z', // Latvian 'Ā': 'A', 'Č': 'C', 'Ē': 'E', 'Ģ': 'G', 'Ī': 'i', 'Ķ': 'k', 'Ļ': 'L', 'Ņ': 'N', 'Š': 'S', 'Ū': 'u', 'Ž': 'Z', 'ā': 'a', 'č': 'c', 'ē': 'e', 'ģ': 'g', 'ī': 'i', 'ķ': 'k', 'ļ': 'l', 'ņ': 'n', 'š': 's', 'ū': 'u', 'ž': 'z' /* jshint ignore:end */ }; /** * Create a web friendly URL slug from a string. * * Requires XRegExp (http://xregexp.com) with unicode add-ons for UTF-8 support. * * Although supported, transliteration is discouraged because * 1) most web browsers support UTF-8 characters in URLs * 2) transliteration causes a loss of information * * @author Sean Murphy * @copyright Copyright 2012 Sean Murphy. All rights reserved. * @license http://creativecommons.org/publicdomain/zero/1.0/ * * @param string s * @param object opt * @return string */ module.exports = function(s, opt) { 'use strict'; s = String(s); opt = Object(opt); var defaults = { 'delimiter': '-', 'limit': undefined, 'lowercase': true, 'replacements': {}, 'transliterate': (typeof(XRegExp) === 'undefined') ? true : false }; // Merge options for (var k in defaults) { if (!opt.hasOwnProperty(k)) { opt[k] = defaults[k]; } } // Make custom replacements for (var j in opt.replacements) { s = s.replace(new RegExp(j, 'g'), opt.replacements[j]); } // Transliterate characters to ASCII if (opt.transliterate) { for (var i in char_map) { s = s.replace(new RegExp(i, 'g'), char_map[i]); } } // Replace non-alphanumeric characters with our delimiter var alnum = (typeof(XRegExp) === 'undefined') ? new RegExp('[^a-z0-9]+', 'ig') : new XRegExp('[^\\p{L}\\p{N}]+', 'ig'); s = s.replace(alnum, opt.delimiter); // Remove duplicate delimiters s = s.replace(new RegExp('[' + opt.delimiter + ']{2,}', 'g'), opt.delimiter); // Truncate slug to max. characters s = s.substring(0, opt.limit); // Remove delimiter from ends s = s.replace(new RegExp('(^' + opt.delimiter + '|' + opt.delimiter + '$)', 'g'), ''); return opt.lowercase ? s.toLowerCase() : s; }; ================================================ FILE: core/lib/utils.js ================================================ 'use strict'; var _ = require('lodash'); module.exports.requireUncached = function (module) { delete require.cache[require.resolve(module)]; return require(module); }; module.exports.extendOptions = function () { var args = Array.prototype.slice.call(arguments); var cb = function (a, b) { if (_.isArray(a)) { return b; } }; args.push(cb); // Don't merge arrays return _.merge.apply(this, args); }; ================================================ FILE: core/lib/viewResolver.js ================================================ 'use strict'; var path = require('path'); var pathResolver = require(path.join(global.pathToApp + '/core/lib/pathResolver.js')); /** * Get information about defined specSpec * * @param {String} typeOrPath - define type of view (spec/navigation), relative path to default views dir or absolute path * @param {Object} viewsOptions - view options (global.opts.rendering.views) * @param {String} [context] - folder dir, to define $(context) resolver * * @returns {String} Return full path to view template of undefined */ module.exports = function(typeOrPath, viewsOptions, context){ if (!typeOrPath) return; var type; // check pre-defined types if (viewsOptions) { for (var typeOption in viewsOptions) { if (typeOption === typeOrPath) { type = typeOrPath; } } // check short path definition if (!type) { var assumedPath = path.extname(typeOrPath) === '.ejs' ? typeOrPath : typeOrPath + '.ejs'; var assumedPathLocation = pathResolver.checkExistence(viewsOptions.defaultViewPaths, assumedPath, context); if (!!assumedPathLocation) { return assumedPathLocation; } } else if (viewsOptions[type] && viewsOptions[type].length > 0){ return pathResolver.checkExistence(viewsOptions[type], null, context); } } return pathResolver.checkExistence([typeOrPath], null, context); }; ================================================ FILE: core/loadOptions.js ================================================ 'use strict'; var fs = require('fs'); var path = require('path'); var configUtils = require('./lib/configUtils'); var utils = require('./lib/utils'); var colors = require('colors'); // jshint ignore:line var silent; var legacyOptionsWarn = function(oldStruct, newStruct, fileName){ var _fileName = fileName || 'options.js'; // Shout warn message only once if (global.legacyOptionsWarnOnce && global.legacyOptionsWarnOnce.indexOf(oldStruct) > -1) return; var msg = 'You are using an old options structure in `' + _fileName + '` conf file, please change ' + oldStruct.red + ' to ' + newStruct.green + '. Old options support will be deprecated in next major release.'; if (global.log) { global.log.warn(msg); } else { global.logQueue = global.logQueue || []; global.logQueue.push({ level: 'warn', msg: msg }); } global.legacyOptionsWarnOnce = global.legacyOptionsWarnOnce || []; global.legacyOptionsWarnOnce.push(oldStruct); }; var legacyOptionsChecker = function(options, fileName){ var haveCommon = options.core && options.core.common; // Server options options.core = options.core || {}; options.core.server = options.core.server || {}; if (haveCommon && options.core.common.port) { if (!silent) legacyOptionsWarn('options.core.common.port', 'options.core.server.port', fileName); options.core.server.port = options.core.server.port || options.core.common.port; // Update old conf path with new (for legacy support) options.core.common.port = options.core.server.port; } // Rendering options options.rendering = options.rendering || {}; if (haveCommon && options.core.common.specFiles) { if (!silent) legacyOptionsWarn('options.core.common.specFiles', 'options.rendering.specFiles', fileName); options.rendering.specFiles = options.rendering.specFiles || options.core.common.specFiles; } // Add *.src.html if not defined if ( options.rendering && options.rendering.specFiles && options.rendering.specFiles.indexOf('index.src') > -1 && options.rendering.specFiles.indexOf('index.src.html') === -1 ) { for (var i=0; i < options.rendering.specFiles.length; i++) { var currentItem = options.rendering.specFiles[i]; if (currentItem === 'index.src') { options.rendering.specFiles.splice(i + 1, 0, 'index.src.html'); break; } } } return options; }; module.exports = function(basePath, _silent){ silent = _silent; var pathToApp = basePath || path.dirname(require.main.filename); var mergedOptions = utils.requireUncached(path.join(pathToApp, 'options')); var pathToUser = path.join(pathToApp, mergedOptions.core.common.pathToUser); // Using specific path to specs parsing, because we don't have global.opts yet var userSettingsFile = path.join(pathToUser, 'options.js'); var userLocalSettingsFile = path.join(pathToUser, 'local-options.js'); // Adding assets npm plugin list to options utils.extendOptions(mergedOptions, configUtils.prepareClientNpmPlugins(pathToUser)); // If user settings file is present, override core settings if(fs.existsSync(userSettingsFile)) { utils.extendOptions(mergedOptions, legacyOptionsChecker(utils.requireUncached(userSettingsFile)), 'options.js'); } // If local settings file is present, override core settings if(fs.existsSync(userLocalSettingsFile)) { utils.extendOptions(mergedOptions, legacyOptionsChecker(utils.requireUncached(userLocalSettingsFile)), 'local-options.js'); } return mergedOptions; }; ================================================ FILE: core/loadPlugins.js ================================================ 'use strict'; /* * * Loading SourceJS plugins * * */ var fs = require('fs'); var path = require('path'); var pathToModules = path.join(global.app.get('user'), 'node_modules'); // Loading all sourcejs-*/core/index.js files from npm plugins section if (fs.existsSync(pathToModules)) { var userModulesFiles = fs.readdirSync(pathToModules); userModulesFiles.map(function(file){ if ((/^sourcejs-/).test(file)) { var pluginIndexPath = path.join(pathToModules, file, 'core','index.js'); if(fs.existsSync(pluginIndexPath)) { try { require(pluginIndexPath); } catch (e) { console.log("User plugins require error: ", e, ' from:', pluginIndexPath); } } } }); } ================================================ FILE: core/logger.js ================================================ 'use strict'; var path = require('path'); var log4js = require('log4js'); var utils = require('./lib/utils'); var fs = require('fs-extra'); var logRootDir = global.pathToApp || __dirname; var customLogLevel = global.commander && global.commander.log ? global.commander.log : undefined; var defaultLogLevel = global.MODE === 'production' ? global.opts.core.common.defaultProdLogLevel : global.opts.core.common.defaultLogLevel; var logLevel = customLogLevel || defaultLogLevel; var config = { prepareLogPath: 'log', log4js: { "appenders": [ { "type": "logLevelFilter", "level": logLevel, "appender": { "type": "console", "layout": { "type": "pattern", "pattern": "%[[%d{yyyy-MM-dd hh:mm:ss}] [%p] -%] %m" } } }, { "type": "clustered", "appenders": [ { "type": "file", "filename": path.join(logRootDir, 'log/app.log'), "maxLogSize": 10485760, "numBackups": 3 }, { "type": "logLevelFilter", "level": "ERROR", "appender": { "type": "file", "filename": path.join(logRootDir, 'log/errors.log') } }, { "type": "logLevelFilter", "level": "WARN", "appender": { "type": "file", "filename": path.join(logRootDir, 'log/warnings.log') } } ], category: 'app' } ] } }; if (global.opts.core.logger) utils.extendOptions(config, global.opts.core.logger); var reloadConf = function(currentConf){ log4js.configure(currentConf); log4js.replaceConsole(log4js.getLogger('app')); }; var addAppenders = function(appendersArr){ appendersArr.forEach(function(item){ config.log4js.appenders.push(item); }); reloadConf(config.log4js); }; var prepareLogDir = function(dir){ // Preparing log dir try { fs.mkdirpSync(path.join(logRootDir, dir)); } catch (e) { if (e.code !== 'EEXIST') { console.error("Could not set up log directory, error: ", e); } } }; // Prepare dafault log path prepareLogDir(config.prepareLogPath); // Configuring log4js reloadConf(config.log4js); var logger = log4js.getLogger('app'); // Example // logger.trace('trace'); // logger.debug('debug'); // logger.info('info'); // console.log('console log'); // logger.warn('warn'); // logger.error('error'); // logger.fatal('fatal'); if (global.logQueue) global.logQueue.forEach(function(item){ logger[item.level](item.msg); }); module.exports = { addAppenders: addAppenders, prepareLogDir: prepareLogDir, // Default logger log: logger, // Configured log4js log4js: log4js }; ================================================ FILE: core/middlewares/clarify.js ================================================ 'use strict'; var path = require('path'); var fs = require('fs-extra'); var url = require('url'); var Q = require('q'); var _ = require('lodash'); var jsdom = require('jsdom'); var ejs = require(path.join(global.pathToApp, 'core/ejsWithHelpers.js')); var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); var pathToApp = path.dirname(require.main.filename); var specUtils = require(path.join(pathToApp, 'core/lib/specUtils')); var parseData = require(path.join(pathToApp, 'core/lib/parseData')); var htmlParser = require(path.join(pathToApp, 'core/html-tree/html-parser')); var htmlDataPath = path.join(pathToApp, global.opts.core.api.htmlData); var parseHTMLData = new parseData({ scope: 'html', path: htmlDataPath }); var htmlParserEnabled = global.opts.plugins && global.opts.plugins.htmlParser && global.opts.plugins.htmlParser.enabled; //TODO JSdoc var getTpl = function(tpl) { var deferred = Q.defer(); var templateName = tpl ? tpl : 'default'; // TODO: use view resolver var pathToTemplate = path.join(pathToApp, 'core/views/clarify', templateName + '.ejs'); var userPathToTemplate = path.join(global.app.get('user'), 'core/views/clarify', templateName + '.ejs'); // First we check user tempalte, then core fs.readFile(userPathToTemplate, 'utf-8', function(err, data){ if (err) { fs.readFile(pathToTemplate, 'utf-8', function(err, data){ if (err) { deferred.reject(err); return; } deferred.resolve(data); }); return; } deferred.resolve(data); }); return deferred.promise; }; var getTplList = function(){ var deferred = Q.defer(); var pathToTemplates = path.join(pathToApp, 'core/views/clarify'); var userPathToTemplates = path.join(global.app.get('user'), 'core/views/clarify'); var templatesList = []; fs.readdir(pathToTemplates, function(err, coreTemplates){ if (err) { deferred.reject({ err: err, msg: 'Could not read directory with Clarify templates' }); return; } coreTemplates.forEach(function(item){ templatesList.push(path.basename(item, '.ejs')); }); fs.readdir(userPathToTemplates, function(err, userTemplates){ if (err) { if (err.code === 'ENOENT') { deferred.resolve(templatesList); } else { deferred.reject({ err: err, msg: 'Could not read user directory with Clarify templates' }); } } else { userTemplates.forEach(function(item){ templatesList.push(path.basename(item, '.ejs')); }); deferred.resolve(_.uniq(templatesList)); } }); }); return deferred.promise; }; // TODO: Move to standalone API, for fast JSDOM spec parsing var parseSpec = function(sections, pathToSpec) { var deferred = Q.defer(); // Parsing spec with JSdom jsdom.env( 'http://127.0.0.1:' + global.opts.core.server.port + pathToSpec + '?internal=true', ['http://127.0.0.1:' + global.opts.core.server.port + '/source/assets/js/modules/sectionsParser.js'], function (err, window) { if (err) { deferred.reject({ err: err, msg: 'JSDOM error' }); return; } var output = {}; var SourceGetSections = window.SourceGetSections; var parser = new SourceGetSections(); var allContents = parser.getSpecFull(); if (sections) { output = parser.getSpecFull(sections); } else { output = allContents; } if (output) { deferred.resolve({ output: output, allContents: allContents }); } else { deferred.reject({ msg: 'Requested sections HTML not found' }); } } ); return deferred.promise; }; var updateApiData = function(specID) { var deferred = Q.defer(); var specs = [specID]; htmlParser.processSpecs(specs, function(err){ if (err) { deferred.reject(err); } else { deferred.resolve(); } }); return deferred.promise; }; var getDataFromApi = function(sections, specID, apiUpdate) { var deferred = Q.defer(); var output = {}; var errMsg = ''; var getSpecData = function(){ var allContents = parseHTMLData.getByID(specID); if (sections) { output = parseHTMLData.getBySection(specID, sections); errMsg = 'Requested sections HTML not found, please define existing sections or update HTML API contects.'; } else { output = allContents; errMsg = 'Requested Spec not found'; } if (output) { deferred.resolve({ output: output, allContents: allContents }); } else { deferred.reject({ msg: errMsg }); } }; if (apiUpdate) { updateApiData(specID).then(function(){ getSpecData(); }).fail(function(err) { var msg = 'Failed updating HTML Spec API. '; deferred.reject({ err: err, msg: msg }); }); } else { getSpecData(); } return deferred.promise; }; var getSectionsIDList = function(sections) { var output = []; var parseContents = function(contents){ for (var i=0; contents.length > i ; i++) { var current = contents[i]; output.push({ header: current.header, id: current.id, visualID: current.visualID }); if (current.nested.length > 0) { parseContents(current.nested); } } }; parseContents(sections.contents); return output; }; module.exports.process = function(req, res, next) { var parsedUrl = url.parse(req.url, true); // Query params var q = parsedUrl.query || {}; var clarifyFlag = q.clarify; // Check if middleware needs to be activated if (clarifyFlag) { var urlPath = parsedUrl.pathname; var parsedPath = specUtils.parseSpecUrlPath(urlPath); var tpl = q.tpl; var fromApi = q.fromApi || false; var apiUpdate = q.apiUpdate || false; var turnOffJS = q.nojs || false; var sections = q.sections ? q.sections.split(',') : undefined; var specInfo = specUtils.getSpecInfo(parsedPath.pathToSpec); var specID = specUtils.getSpecIDFromUrl(parsedPath.pathToSpec); var specHasHTMLAPIData = !!parseHTMLData.getByID(specID); var ua = req.headers && req.headers['user-agent'] ? req.headers['user-agent'] : undefined; trackStats.page({ pageName: 'clarify', sessionID: trackStats.getSessionID(req), ua: ua }); if (!specInfo) { res.send('Clarify did not found any information about requested spec, please check URL or update file-tree restarting the app.'); return; } if (!specHasHTMLAPIData) apiUpdate = true; var getSpecData = function(){ return fromApi ? getDataFromApi(sections, specID, apiUpdate) : parseSpec(sections, parsedPath.pathToSpec); }; Q.all([ getSpecData(), getTplList() ]).spread(function(_specData, tplList) { var specData = _specData.output; var sections = specData.contents ? specData.contents : []; if (sections.length > 0) { var checkHeadResources = function(specData, target){ return specData.headResources && specData.headResources[target]; }; var checkBodyResources = function(specData, target){ return specData.bodyResources && specData.bodyResources[target]; }; var clarifyData = ''; var templateJSON = { nojs: turnOffJS, title: specInfo.title, sections: sections, headCssLinks: checkHeadResources(specData, 'cssLinks') ? specData.headResources.cssLinks.join('\n') : '', headScripts: checkHeadResources(specData, 'scripts') ? specData.headResources.scripts.join('\n'): '', headCssStyles: checkHeadResources(specData, 'cssStyles') ? specData.headResources.cssStyles.join('\n') : '', bodyCssLinks: checkBodyResources(specData, 'cssLinks') ? specData.bodyResources.cssLinks.join('\n') : '', bodyScripts: checkBodyResources(specData, 'scripts') ? specData.bodyResources.scripts.join('\n'): '', bodyCssStyles: checkBodyResources(specData, 'cssStyles') ? specData.bodyResources.cssStyles.join('\n') : '', clarifyData: clarifyData }; getTpl(tpl).then(function(tpl){ var html = ''; try { html = ejs.render(tpl, templateJSON); } catch (err) { var msg = 'Clarify: ERROR with EJS rendering failed'; if (global.MODE === 'development') global.log.error(msg + ': ', err); html = msg; } res.send(html); }).fail(function(err) { var msg = 'ERROR: Could not find requested or default template for Clarify'; if (global.MODE === 'development') global.log.warn('Clarify: ' + msg + ': ', err); res.status(500).send(msg); }); } else { res.send('Clarify did not found any of requested sections.'); } }).fail(function(errData) { global.log.warn('Clarify: ' + (errData.msg || 'Error in data preparation'), errData); res.status(500).send(errData.msg); }); } else { // redirect to next express middleware next(); } }; ================================================ FILE: core/middlewares/loader.js ================================================ 'use strict'; var _ = require('lodash'); var fs = require('fs'); var path = require('path'); var appRoot = global.pathToApp; var utils = require(path.join(appRoot, 'core/lib/utils')); var log = require(path.join(appRoot, 'core/logger')).log; var gatherMiddlewares = function(dest, filterRegExp, mainJS){ var output = {}; if (fs.existsSync(dest)) { var userMiddlewareFiles = fs.readdirSync(dest); userMiddlewareFiles.map(function (dir) { if (!filterRegExp || filterRegExp.test(dir)) { var middlewareName = dir; var _mainJS = mainJS || 'index.js'; var pluginIndexPath = path.join(dest, dir, _mainJS); if (fs.existsSync(pluginIndexPath)) { output[middlewareName] = { enabled: true, order: 0, group: 'default', indexPath: pluginIndexPath }; // Load middleware options var configPath = path.join(dest, dir, 'options.js'); if (fs.existsSync(configPath)) { var middlewareConfig = require(configPath); _.forOwn(output[middlewareName], function (value, key) { var overVal = middlewareConfig[key]; if (overVal) { if (key === 'order') { if (overVal >= 0) { output[middlewareName][key] = overVal; } else { log.warn('Middlewares are restricted to define order with value lower than 0 (zero). Please modify '+ middlewareName+ ' middleware options.'); } } else { output[middlewareName][key] = overVal; } } }); } } } }); } return output; }; var sortMiddlewares = function(groupsOrder, list){ var output = []; if (!(groupsOrder && _.isArray(groupsOrder) && list)) return output; var groupedList = {}; // Sort by groups _.forOwn(list, function (value, key) { var group = value.group || 'default'; var middleware = value; if (!middleware.enabled) return; middleware.name = middleware.name || key; groupedList[group] = groupedList[group] || []; groupedList[group].push(value); }); // Sort each group _.forOwn(groupedList, function (value, key) { groupedList[key] = _.sortByOrder(value, ['order'], ['asc']); }); // Concat groups by order groupsOrder.forEach(function(item){ if (groupedList[item]) output = output.concat(groupedList[item]); }); return output; }; var loadMiddlewares = function(listArr, app){ if (!_.isArray(listArr) && !app) return; listArr.forEach(function(item){ if (item && item.indexPath && fs.existsSync(item.indexPath)) { log.debug('require middleware', item.indexPath); app.use(require(item.indexPath).process); } }); }; module.exports.process = function(app, globalOptions){ var config = { loadGroupsOrder: [ 'request', 'pre-html', 'default', 'html', 'response' ], list: { md: { enabled: true, order: -1, group: 'pre-html', indexPath: path.join(appRoot, 'core/middlewares/md.js') }, mdTag: { enabled: true, order: 0, group: 'html', indexPath: path.join(appRoot, 'core/middlewares/mdTag.js') }, clarify: { enabled: true, order: -2, group: 'request', indexPath: path.join(appRoot, 'core/middlewares/clarify.js') }, read: { enabled: true, order: -1, group: 'request', indexPath: path.join(appRoot, 'core/middlewares/read.js') }, send: { enabled: true, order: -1, group: 'response', indexPath: path.join(appRoot, 'core/middlewares/send.js') }, wrap: { enabled: true, order: -1, group: 'html', indexPath: path.join(appRoot, 'core/middlewares/wrap.js') } } }; utils.extendOptions( config, { list: gatherMiddlewares(path.join(app.get('user'), 'node_modules'), new RegExp(/^sourcejs-/), 'core/middleware/index.js') }, globalOptions.core.middlewares ); loadMiddlewares(sortMiddlewares(config.loadGroupsOrder, config.list), app); }; ================================================ FILE: core/middlewares/md.js ================================================ 'use strict'; var path = require('path'); var prettyHrtime = require('pretty-hrtime'); var processMd = require(path.join(global.pathToApp,'core/lib/processMd')); /* * Get file content from response and parse contained Markdown markup * * @param {object} req - Request object * @param {object} res - Response object * @param {function} next - The callback function * */ exports.process = function (req, res, next) { if (req.specData && req.specData.renderedHtml && req.specData.isMd) { var start = process.hrtime(); var plainMarkdown = req.specData.renderedHtml; req.specData.renderedHtml = processMd(plainMarkdown, { wrapDescription: true }); var end = process.hrtime(start); global.log.debug('Markdown processing took: ', prettyHrtime(end)); req.specData.isMd = false; next(); } else { next(); } }; ================================================ FILE: core/middlewares/mdTag.js ================================================ 'use strict'; var path = require('path'); var processMd = require(path.join(global.pathToApp,'core/lib/processMd')); /* * Get html from response and parse contained Markdown markup * * @param {object} req - Request object * @param {object} res - Response object * @param {function} next - The callback function * */ exports.process = function (req, res, next) { if (req.specData && req.specData.renderedHtml) { var html = req.specData.renderedHtml; /* find text wrapped with */ html = html.replace(/([\s\S]*?)<\/markdown>/g, function(match) { // strip tags match = match.replace(/|<\/markdown>/g, ''); // Remove tabs and multiple spaces match = match.replace(/\t/g, '').replace(/ +(?= )/g, ''); // render markdown return processMd(match); }); req.specData.renderedHtml = html; } next(); }; ================================================ FILE: core/middlewares/read.js ================================================ 'use strict'; var fs = require('fs-extra'); var path = require('path'); var ejs = require(path.join(global.pathToApp, 'core/ejsWithHelpers.js')); var specUtils = require(path.join(global.pathToApp,'core/lib/specUtils')); var configUtils = require(path.join(global.pathToApp,'core/lib/configUtils')); //var config = {}; /** * Checking if Spec is requested * * @param {object} req - Request object * @param {object} res - Response object * @param {function} next - The callback function * */ exports.process = function(req, res, next) { var reqExt = path.extname(req.path); // Skip static resources, and dirs witout trailing slash if (reqExt !== "" || (reqExt === "" && req.path.slice(-1) !== '/')) { next(); return; } var apiRe = new RegExp('^/api/'); var sourceRe = new RegExp('^/sourcejs/'); // Check if folder is requested but not the reserved namespaces if (!apiRe.test(req.path) && !sourceRe.test(req.path)) { global.specLoadTime = process.hrtime(); var specPath = specUtils.getFullPathToSpec(req.path); var contextOptions = configUtils.getContextOptions(req.path); var specFiles = contextOptions.specInfo && contextOptions.specInfo.specFile ? [contextOptions.specInfo.specFile] : contextOptions.rendering.specFiles; var physicalPath = specUtils.getSpecFromDir(specPath, specFiles); var specFile = typeof physicalPath === 'string' ? path.basename(physicalPath) : undefined; if (specFile === 'index.html' || !physicalPath) { next(); return; } // Passing req params var urlParams = req.url.split('?')[1]; var paramsString = urlParams ? '?' + urlParams : ''; // Modifying url and saving params string // TODO: remove in next non-patch release https://github.com/sourcejs/Source/issues/147 req.originalUrl = req.url; req.originalPath = req.path; req.url = path.join(req.path, specFile) + paramsString; fs.readFile(physicalPath, 'utf8', function (err, data) { if (err) { res.send(err); return; } // Filled during middleware processing req.specData = {}; var specInfo = contextOptions.specInfo || { title: 'No '+ contextOptions.core.common.infoFile +' defined' }; var capitalizedExtension; if (/.src.html$/.test(physicalPath)) { capitalizedExtension = 'Src'; } else { var specFileExtension = path.extname(physicalPath).replace(".", ""); capitalizedExtension = specFileExtension.charAt(0).toUpperCase() + specFileExtension.slice(1); } data = data.replace(/^\s+|\s+$/g, ''); // Pre-render Spec contents with EJS if (!specInfo.noEjs) { try { data = ejs.render(data, { engineVersion: global.engineVersion, info: specInfo }, { filename: physicalPath }); } catch(err){ global.log.warn('Could not pre-render spec with EJS: ' + req.path, err); } } req.specData["is" + capitalizedExtension] = true; req.specData.info = specInfo; // add spec info object to request req.specData.contextOptions = contextOptions; // add context options to request req.specData.renderedHtml = data; // add spec content to request next(); }); } else { next(); } }; ================================================ FILE: core/middlewares/send.js ================================================ 'use strict'; var path = require('path'); var prettyHrtime = require('pretty-hrtime'); var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); /** * In case if request contains rendered html, then send it as response and stop spec content post-processing. * * @param {object} req - Request object * @param {object} res - Response object * @param {function} next - The callback function * */ exports.process = function (req, res, next) { if (req.specData && req.specData.renderedHtml) { trackStats.specs(req); global.log.trace('Spec loading time: ', prettyHrtime(process.hrtime(global.specLoadTime))); res.send(req.specData.renderedHtml); } else { next(); } }; ================================================ FILE: core/middlewares/wrap.js ================================================ 'use strict'; var fs = require('fs-extra'); var path = require('path'); var cheerio = require('cheerio'); var ejs = require(path.join(global.pathToApp, 'core/ejsWithHelpers.js')); var viewResolver = require(path.join(global.pathToApp + '/core/lib/viewResolver.js')); var getHeaderAndFooter = require(global.pathToApp + '/core/headerFooter.js').getHeaderAndFooter; var specUtils = require(path.join(global.pathToApp,'core/lib/specUtils')); /** * Wrap rendered html from request with spec wrapper (header, footer, etc.) * * @param {object} req - Request object * @param {object} res - Response object * @param {function} next - The callback function * */ exports.process = function (req, res, next) { // Check if we're working with processed file if (req.specData && req.specData.renderedHtml) { var specDir = specUtils.getFullPathToSpec(req.url); var contextOptions = req.specData.contextOptions; // get spec content var data = req.specData.renderedHtml.replace(/^\s+|\s+$/g, ''); // get spec info var info = req.specData.info; // choose the proper template, depending on page type or defined path var viewParam = 'spec'; var context; if (info.template) { viewParam = info.template; context = specDir; } else if (info.role === 'navigation') { viewParam = 'navigation'; } var templatePath = viewResolver(viewParam, contextOptions.rendering.views, context) || viewParam; fs.readFile(templatePath, "utf-8", function(err, template){ if (err) { res.send('EJS template "' + templatePath + '" not found. Please check view configuration.'); return; } var $ = cheerio.load(data, {decodeEntities: false}); var head = ''; var $headHook = $('head'); if ($headHook.length > 0) { head = $headHook.html(); $('head').remove(); } var content = ''; var $body = $('body'); var $html = $('html'); if ($body.length > 0) { content = $body.html(); } else if ($html.length > 0) { content = $html.html(); } else { content = $.html(); } var heagerFooter = getHeaderAndFooter(); // final data object for the template var templateJSON = { engineVersion: global.engineVersion, content: content, head: head, info: info }; try { templateJSON.header = ejs.render(heagerFooter.header, templateJSON); } catch(err){ var headerMsg = 'Error: EJS could render header template: ' + heagerFooter.headerPath; templateJSON.header = headerMsg; global.log.warn(headerMsg, err.stack); } try { templateJSON.footer = ejs.render(heagerFooter.footer, templateJSON, { filename: heagerFooter.footerPath }); } catch(err){ var footerMsg = 'Error: EJS could render footer template: ' + heagerFooter.footerPath; templateJSON.footer = footerMsg; global.log.warn(footerMsg, err.stack); } // render page and send it as response try { req.specData.renderedHtml = ejs.render(template, templateJSON, { filename: templatePath }); } catch(err){ req.specData.renderedHtml = 'Error rendering Spec with EJS: ' + template; } req.specData.renderedHtml += '\n'; next(); }); } else { // proceed to next middleware next(); } }; ================================================ FILE: core/postInstall.js ================================================ 'use strict'; var path = require('path'); var fs = require('fs'); var link = require('./lib/createLink'); var pathToApp = __dirname.replace(/^\w:\\/, function (match) { return match.toLowerCase(); }); var enginePath = path.join(pathToApp, '../'); var userPath = path.join(enginePath, '../../'); var internalUserPath = path.join(enginePath, 'user'); var parentNodeModules = path.join(enginePath, '../'); // check if sourcejs is installed as a node_package if ( path.relative(parentNodeModules, enginePath) === 'sourcejs' && fs.existsSync(path.join(userPath, 'options.js')) ) { link(userPath, internalUserPath, 'dir'); console.log('SourceJS User folder symlink created.'); } ================================================ FILE: core/routes/index.js ================================================ 'use strict'; // Core routes require("./redirects.js"); // User custom routes try { require(global.app.get('user') + "/core/routes"); } catch(e){} ================================================ FILE: core/routes/redirects.js ================================================ 'use strict'; /* * * This file contains default redirects, to add custom redirects user /user/core/routes/index.js * * */ var path = require('path'); var express = require('express'); var pathToApp = path.dirname(require.main.filename); /* * * Static routing for SourceJS assets include custom config and follow this priority queue: * 1. First check if we have overridden assets from `sourcejs/user/source/assets` * 2. Then check if we have minified version of assets in `sourcejs/build/assets` * 3. And if nothing found before, app send original assets from `source/assets` folder * * Engine main build script also minifies CSS and HTML from `sourcejs/user/assets` and puts it in `sourcejs/build/user/assets` * * */ // Check overrides from user folder global.app.use('/source/assets', express.static(path.join(pathToApp, global.opts.core.common.pathToUser, 'source/assets'))); // Check if there's minified assets global.app.use('/source/assets', express.static(path.join(pathToApp,'build/assets'))); global.app.use('/assets', express.static(path.join(pathToApp, 'build', global.opts.core.common.pathToUser, 'assets'))); // Redirecting core client-side file requests to app root paths global.app.use('/source/assets', express.static(path.join(pathToApp, 'assets'))); // Custom routes global.app.use('/docs', express.static(path.join(pathToApp, '/docs'))); global.app.use('/jsdoc', express.static(path.join(pathToApp, '/jsdoc'))); global.app.use('/test', express.static(path.join(pathToApp, '/assets/test'))); ================================================ FILE: core/trackInstall.js ================================================ 'use strict'; var path = require('path'); var rootPath = __dirname.replace(/^\w:\\/, function (match) { return match.toLowerCase(); }); var enginePath = global.pathToApp = path.join(rootPath, '../'); var loadOptions = require('./loadOptions'); var options = global.opts = loadOptions(enginePath); var trackStats = require(path.join(global.pathToApp, 'core/trackStats')); if (process.env.CI) { options.core.tracking.enabled = false; } if (options && options.core && options.core.tracking && options.core.tracking.enabled) { trackStats.event({ group: 'install', event: 'default' }); console.log('[SOURCEJS] Note: engine tracks anonymous usage statistics. To disable it, edit `core.tracking` configuration.'); } else { trackStats.event({ group: 'install', event: 'disabled tracking' }, true); } ================================================ FILE: core/trackStats.js ================================================ 'use strict'; var url = require('url'); var _ = require('lodash'); var path = require('path'); var device = require('device'); var macaddress = require('macaddress'); var ua = require('universal-analytics'); var log = require(path.join(global.pathToApp, 'core/logger')).log; var utils = require(path.join(global.pathToApp, 'core/lib/utils')); var crypto = require('crypto'); var config = { enabled: true, trackingId: 'UA-66924051-2' }; utils.extendOptions( config, global.opts.core.tracking ); if (process.env.CI || (global.commander && global.commander.test)) { config.enabled = false; } var generateMachineID = function(){ var macNums = macaddress.networkInterfaces(); var unique = ''; _.forOwn(macNums, function(val){ if (val.mac) { unique += val.mac; } else if (val.ipv4) { unique += val.ipv4; } }); return 'host-' + crypto.createHash('md5').update(unique).digest('hex').slice(0, 5); }; var machineID = generateMachineID(); var hostVisitor = ua(config.trackingId, machineID, {strictCidFormat: false}); var _trackPage = function(opts){ if ( !config.enabled || !opts.pageName || (opts.ua && device(opts.ua).is('bot')) ) return; var visitor = hostVisitor; var uid = machineID; if (opts.sessionID) { uid = opts.sessionID; visitor = ua(config.trackingId, uid, {strictCidFormat: false}); } var payload = { dp: opts.pageName, ds: machineID, cs: machineID, cid: uid, uid: uid }; log.trace('track page', opts.pageName); log.trace('as a visitor', visitor); if (opts.ua) payload.ua = opts.ua; visitor.pageview(payload).send(); }; // Track host-initiated events (by unique machine id) var _trackEvent = function(opts, force){ force = process.env.CI ? false : force; if (!force && !config.enabled || !opts.event) return; var visitor = hostVisitor; var uid = machineID; var group = opts.group || 'default'; if (opts.sessionID) { uid = opts.sessionID; visitor = ua(config.trackingId, uid, {strictCidFormat: false}); } log.trace('track event', group + ':' + opts.event); log.trace('as a visitor', visitor); visitor.event({ ec: group, ea: opts.event, ds: machineID, cs: machineID, cid: uid, uid: uid }).send(); }; var getSessionID = module.exports.getSessionID = function(req) { var sessionID = req.sessionID; var host = req.headers && req.headers.host ? req.headers.host : undefined; if (host && (/^localhost/.test(host) || /^127.0.0.1/.test(host))) { sessionID = machineID; } else if (req.cookies && req.cookies['source-track']) { sessionID = req.cookies['source-track']; } return sessionID; }; // Track specs module.exports.specs = function(req) { var pageName = 'spec'; var parsedUrl = url.parse(req.url, true); var q = parsedUrl.query || {}; var ua = req.headers && req.headers['user-agent'] ? req.headers['user-agent'] : undefined; if (q.internal) return; if (req.originalPath === '/') { pageName = '/'; } else if (req.specData && req.specData.info && req.specData.info.role === 'navigation') { pageName = 'navigation'; } _trackPage({ sessionID: getSessionID(req), pageName: pageName, ua: ua }); }; module.exports.page = _trackPage; module.exports.event = _trackEvent; ================================================ FILE: core/unflat.js ================================================ 'use strict'; function unflatten(target, opts) { opts = opts || {}; var delimiter = opts.delimiter || '.', result = {}; if (Object.prototype.toString.call(target) !== '[object Object]') { return target; } // safely ensure that the key is // an integer. function getkey(key) { var parsedKey = Number(key); return (isNaN(parsedKey) || key.indexOf('.') !== -1)? key : parsedKey; } Object.keys(target).forEach(function(key) { var split = key.split(delimiter), key1 = getkey(split.shift()), key2 = getkey(split[0]), recipient = result; while (key2 !== undefined) { if (recipient[key1] === undefined) { recipient[key1] = ( (typeof key2 === 'number' && !opts.object)? [] : {}); } recipient = recipient[key1]; if (split.length > 0) { key1 = getkey(split.shift()); key2 = getkey(split[0]); } } // unflatten again for 'messy objects' if (recipient[key1] === undefined) recipient[key1] = unflatten(target[key], opts); // if there is already exist field that may // be overwritten with low-level structure else if (opts.overwrite) recipient[key1][opts.overwrite] = unflatten(target[key], opts); }); return result; } module.exports = unflatten; ================================================ FILE: core/views/404.ejs ================================================ 404
      <%- header %>

      404

      Page not found
      <%- footer %>
      ================================================ FILE: core/views/auth-done.ejs ================================================ Github OAuth Loading ================================================ FILE: core/views/clarify/clear.ejs ================================================ <%= title %> <%- headCssLinks %> <%- headCssStyles %> <% if(!nojs) { %> <%- headScripts %> <% } %>
      <% for(var i = 0; i < sections.length; i++) { %> <% for(var j = 0; j < sections[i].html.length; j++) { %> <%- sections[i].html[j] %> <% } %> <% } %>
      <%- bodyCssLinks %> <%- bodyCssStyles %> <% if(!nojs) { %> <%- bodyScripts %> <% } %> ================================================ FILE: core/views/clarify/default.ejs ================================================ <%= title %> <%- headCssLinks %> <%- headCssStyles %> <% if(!nojs) { %> <%- headScripts %> <% } %>
      <% for(var i = 0; i < sections.length; i++) { %>

      <%- sections[i].visualID %>. <%- sections[i].header %>

      <% for(var j = 0; j < sections[i].html.length; j++) { %>
      <%- sections[i].html[j] %>
      <% } %> <% } %>
      <%- bodyCssLinks %> <%- bodyCssStyles %> <% if(!nojs) { %> <%- bodyScripts %> <% } %> <%- clarifyData %> ================================================ FILE: core/views/clean-spec.ejs ================================================ <%= info.title %> <% if (info.keywords !== '') {%> <% } %> <%- head %>
      <%- header %>
      <%- content %>
      <%- footer %>
      ================================================ FILE: core/views/doc.ejs ================================================ <%= info.title %> <% if (info.keywords !== '') {%> <% } %> <%- head %>
      <%- header %>

      <%= info.title %>

      v.<%- engineVersion %>
      <%- content %>
      <%- footer %>
      ================================================ FILE: core/views/navigation.ejs ================================================ <%= info.title %> <%- head %>
      <%- header %>
      <%- content %>
      <%- footer %>
      ================================================ FILE: core/views/spec.ejs ================================================ <%= info.title %> <% if (info.keywords !== '') {%> <% } %> <%- head %>
      <%- header %>
      <%- content %>
      <%- footer %>
      ================================================ FILE: core/watch/childWatch.js ================================================ // Runs as a child process through forever-monitor 'use strict'; var path = require('path'); var watch = require('gulp-watch'); var deepExtend = require('deep-extend'); var commander = require('commander'); // Parse arguments commander .option('-r, --root [string]', 'Root path of the SourceJS app', '../../') .option('-l, --log [string]', 'Log level', 'info') .parse(process.argv); global.commander = global.commander || commander; // Normalized path to master app global.pathToApp = global.pathToApp || path.join(__dirname, commander.root).replace(/\\/g, '/').replace(/\/$/, ''); var loadOptions = require(path.join(global.pathToApp, 'core/loadOptions')); global.opts = global.opts || loadOptions(path.resolve(global.pathToApp), true); var logger = require(path.join(global.pathToApp, 'core/logger')); var log = logger.log; global.log = log; var utils = require(path.join(global.pathToApp, 'core/lib/utils')); var fileTree = require(path.join(global.pathToApp, 'core/file-tree')); var config = { enabled: true, verbose: false, glob: [ '!' + global.pathToApp + '/**/.*/**', '!' + global.pathToApp + '/**/bower_components/**', '!' + global.pathToApp + '/**/node_modules/**', global.pathToApp + '/**/' + global.opts.core.common.infoFile, global.pathToApp + '/' + global.opts.core.common.pathToUser + '/**/' + global.opts.core.common.infoFile ], ignoreHiddenFiles: true }; // Overwriting base options utils.extendOptions(config, global.opts.core.watch); var prepareData = function(data, targetFile){ var dir = path.dirname(targetFile); return deepExtend(fileTree.getSpecMeta(dir), data); }; if (config.enabled){ if (!global.opts.core.watch.foreverWatchEnabled) { global.log.debug('Specs watcher process started.'); } watch(config.glob, { verbose: config.verbose }, function(vinyl){ var filePath = vinyl.path.replace(/\\/g, '/'); var event = vinyl.event; var cleanPath = filePath.replace(global.pathToApp + '/', '').replace(global.opts.core.common.pathToUser +'/', ''); var specID = path.dirname(cleanPath); var rawContents; global.log.debug('event', event); global.log.debug('changed file', filePath); try { rawContents = JSON.parse(String(vinyl.contents)); } catch(e) {} // TODO: run full re-index on huge changes (like git update) if (rawContents && (event === 'change' || event === 'add')) { var dataToSend = {}; dataToSend[specID + '/specFile'] = prepareData(rawContents, filePath); fileTree.updateFileTree(dataToSend, true); } if (typeof rawContents === 'undefined' || event === 'unlink') { fileTree.deleteFromFileTree(specID); } }); } ================================================ FILE: core/watch/index.js ================================================ 'use strict'; var forever = require('tinyforever'); var path = require('path'); var utils = require(path.join(global.pathToApp, 'core/lib/utils')); var config = { enabled: true, forever: { silent: false, max: 10, minUptime: 1000, args: ['--log='+global.commander.log, '--root=../../'] } }; // Overwriting base options utils.extendOptions(config, global.opts.core.watch); if (config.enabled) { var child = new (forever.Monitor)(path.join(global.pathToApp, 'core/watch/childWatch.js'), config.forever); child.on('start', function() { global.log.debug('Specs watcher process started with forever.'); }); child.on('stderr', function() { global.log.warn('Specs watcher error:'); }); child.on('restart', function() { global.log.debug('Restarting the Specs watcher process...'); }); child.on('exit', function () { global.log.error('Specs watcher stopped. Re-run app to activate navigation watcher.'); }); child.start(); process.on('exit', function () { child.stop(); }); } ================================================ FILE: docs/README.md ================================================ Engine documentation comes packed to every SourceJS instance, run `/docs` section locally or visit http://sourcejs.com/docs. ================================================ FILE: docs/api/index.src.html ================================================

      API documentation

      Go to engine documentation ================================================ FILE: docs/api/info.json ================================================ { "title": "API documentation", "role": "navigation" } ================================================ FILE: docs/api/load-events/info.json ================================================ { "keywords": "events, spec loaded", "title": "Spec Load Event", "template": "doc" } ================================================ FILE: docs/api/load-events/readme.md ================================================ Spec load event API helps to organize Spec loading flow, and define the final load event of all content, modules and plugins. ## The Problem As SourceJS is modular engine, any Spec page could be featured with many client-side plugins, which will operate with DOM during the page load. To resolve those cases, when modules or plugins need to interact with each other, or wait till all DOM operations are done, we made Spec Load Event. Load event is recommended to use in any SourceJS plugins that make changes to DOM during Spec load. Currently used by HTML API for PhantomJS, and various client-side plugins. Example use cases: * When Specs page is loaded through PhantomJS, we must know when page is done building, considering all DOM interactions from plugins * When we open Spec page with link to section, we must be sure, that page scroll will remain in the right position after load ## Basics Each plugin that interacts with DOM must be registered at Load Event API global object `window.source.loadEvents`. Recommended plugin definition: ```js window.source = window.source || {}; window.source.loadEvents = window.source.loadEvents || {}; window.source.loadEvents.moduleName = window.source.loadEvents.moduleName || { timeout: '300', finishEvent: 'moduleNameFinished', updateEvent: 'moduleNameFinishedUpdated' }; ``` As module load sequence could be random, first we init main data object `window.source.loadEvent`, then define options for our module. Note: if you are using RequireJS, then be sure that module is registered before any `define`/`require` call. ```js window.source = window.source || {}; ... define([], function(){ ... }); ``` ## Options Registering new module to Load Event API we pass these options: | Key | Type | Description | |---|---|---| | finishEvent | String | Name of the Finish event, that registered module will emit, when finishes it's work with DOM. | | updateEvent | String | Name of the Update event, that module will emit to postpone ready state and reset timeout counter. | | [timeout] | Number | Timeout (optional, by default is set to `1000`) is used to define approximate eval time. | Emit `finishEvent`, when you know that your module done working with the DOM, if it won't be called, API will rely on timeout. The default timeout is always used for any registered module, when it's passed, we consider that module is done his job. If the module is working with DOM quite actively with few iterations, it's recommended to emit `updateEvent`, to reset timeout counter on each iteration. ## Emitting Events Recommended way of emitting `finishEvent` or `updateEvent` is: ```js if (window.CustomEvent) { new CustomEvent('moduleNameFinished'); } else { var event = document.createEvent('CustomEvent'); event.initCustomEvent('moduleNameFinished', true, true); } ``` Where event name `moduleNameFinished` is the same as we defined during module registration. ## Global Finish Event When all registered modules to Load Event API are loaded, or timeout is passed, API emits event named `allPluginsFinish`. ### Check plugin status without event If event was shouted before you subscribe, you can check global storage object for any status of registered plugins. ```js window.source.loadEvents.moduleName.finish window.source.loadEvents.pluginName.finish ``` ## Grouping modules To define module bundles, and control their load state, you can also define a group with array of module names: ```js window.source = window.source || {}; window.source.loadEvents = window.source.loadEvents || {}; window.source.loadEvents.groupName1 = ['moduleName1', 'pluginName2']; ``` When all modules and plugins in group are done their job, we emit `groupName1GroupFinish` event, where `groupName1` is your defined name. ================================================ FILE: docs/api/plugins/info.json ================================================ { "keywords": "plugins, middlewares", "title": "Writing SourceJS Plugins and Middlewares", "template": "doc" } ================================================ FILE: docs/api/plugins/readme.md ================================================ SourceJS core contains only default APIs for most common use cases, all specific features we move to plugins, that could contain back-end and client-side improvements. ## Starting Templates To easily bootstrap new SourceJS items, we extended our official [SourceJS Generator](https://github.com/sourcejs/generator-sourcejs) with default templates for all types of engine components: ```html $ yo sourcejs:plugin $ yo sourcejs:middleware ``` Besides generators, we also have abstract plugin demos, which will be always updated to current engine APIs: * [sourcejs-tpl-plugin](https://github.com/sourcejs/sourcejs-tpl-plugin) * [sourcejs-tpl-middleware](https://github.com/sourcejs/sourcejs-tpl-middleware) We are continuously improving mentioned demos, adding new best practices and API usage examples. ## SourceJS Plugins and Middleware Structure The recommended way of structuring SourceJS plugins is to define them as [NPM packages](https://docs.npmjs.com/misc/developers). All public plugins must be named by corresponding prefix `sourcejs-*`, so they could be easily searchable through global repository. All plugins must be installed in `sourcejs/user` directory: ```html /source user package.json node_modules sourcejs-plugin1 sourcejs-plugin2 ``` ### Internal plugins SourceJS is an open source project, and we expect our community to be open as we do, sharing their features and engine extends to public. But in case you prefer to have private plugins, there are few ways solving this case: * Create private NPM plugins, installing them from your closed repositories * Commit `sourcejs/user/node_modules` contents to your common repository * Using `sourcejs/user/plugins` folder for your custom client-side dependencies and `sourcejs/user/app.js` to extend SourceJS back-end Last mentioned option is deprecated, and not recommended to use, yet you can still find some mentions of this approach in `options.js` and `moduleLoader.js`. ## Plugins ### Client-side The entry point of plugins client-side is `assets/index.js` file in root of plugin folder. Each plugin works the same as any internal JavaScript module and is loaded through [RequireJS](http://requirejs.org/) using `moduleLoader.js`. With only one difference - internal modules must be enabled in `options.js` (`assets.modulesEnabled`) but plugin JavaScript modules are enabled by default. From your plugin, you can call any libraries and internal APIs, defining your dependencies in AMD module: ```js define([ 'jquery', 'sourceModules/module', 'sourceModules/css' ], function ($, module, css) {}); ``` To access other plugin assets, you can use direct path to your component: ```js define([ 'node_modules/sourcejs-plugin/assets/js/innerDep', 'text!node_modules/sourcejs-plugin/assets/templates/tpl.html' ], function (innerDep, tpl) {}); ``` #### Connecting Plugin JS Modules As we mentioned above, when SourceJS plugin is installed through NPM, main client-side module of your plugin (`index.js`) is connected by default. To achieve this, we generate custom RequireJS configuration through default build task and dynamically extend `options.js`, filling installed modules to `assets.npmPluginsEnabled` with `true`. To disable client-side module of any installed npm Plugins, you can edit the module definition in `user/options.js`: ```js { core: {}, assets: { npmPluginsEnabled: { 'sourcejs-plugin': false } } } ``` ### Back-end Plugins back-end is exposed in `core/index.js` and is automatically loaded during SourceJS app initialization, before importing `user/app.js` extender. It's a simple Node.js module, from which you can use any existing dependencies in SourceJS or define your own. As SourceJS back-end uses [ExpressJS](http://expressjs.com), it's recommended to use this framework for creating APIs and custom routers. ### Examples * [sourcejs-react](https://www.npmjs.com/package/sourcejs-react) - rendering React components in specs * [sourcejs-bubble](https://github.com/sourcejs/sourcejs-bubble) - comments for examples (have client-side and back-end features) * [sourcejs-spec-dependencies](https://github.com/sourcejs/sourcejs-spec-dependencies) - exposing Spec Dependencies (have client-side and back-end features) * [sourcejs-spec-status](https://github.com/sourcejs/sourcejs-spec-status) * [sourcejs-specs-linting](https://github.com/sourcejs/sourcejs-specs-linting) ## Middlewares SourceJS Middleware is basically the same as Plugin item, but it has only back-end part and its entry point must be defined in `core/middleware/index.js` path. As SourceJS uses [ExpressJS](http://expressjs.com), middleware development constraints are bound to this framework. Most common use cases of middleware in SourceJS in modification of Spec file contents during the request. ### Connecting The Middleware Middlewares are automatically loaded after installation, and are evaluated on each request before `sourcejs/core/middleware/wrap.js` and `sourcejs/core/middleware/send.js`. `wrap.js` is wrapping spec page (`index.src.html`, `index.md` and etc) contents in a pre-defined a view template from `sourcejs/core/views` or user custom path `sourcejs/user/core/views` using [EJS](http://www.embeddedjs.com/). `send.js` is used in case when we modify Spec contents, as we do with `*.src.html` and all other middlewares. Modified Spec content is passed through `req.specData.renderedHtml` object, which each middleware can modify during the request handling process and which then is sent to the client's browser. ### Modifying Spec contents We alredy mentioned before that Middlewares use `req.specData.renderedHtml` object to get processed Spec contents and modify them. As any other basic ExpressJS middleware, at the processing start, we get `req` object on input and pass it to the next handler on each request. This architecture allows us to modify `req` contents on each step. In 0.4.0 middlewares from plugins are connected one by one, sorted by alphabet. In nearest releases we will add a feature for controlling the queue. ### Examples * [sourcejs-jade](https://github.com/sourcejs/sourcejs-jade) * [sourcejs-smiles](https://github.com/sourcejs/sourcejs-smiles) ================================================ FILE: docs/api/readme.md ================================================ Internal API documentation. ================================================ FILE: docs/api/rest-api/info.json ================================================ { "keywords": "api, rest", "title": "REST API", "template": "doc" } ================================================ FILE: docs/api/rest-api/readme.md ================================================ From 0.4.0 version, SourceJS started to grow own REST API for flexible plugins development and easy side services integration. API provides full access to Spec contents, navigation tree and other useful features. ## GET Specs [Get](/api/specs) access to Specs navigation tree with filtering by various options. This Navigation tree is used to build search, catalog pages and digests, here you can also read all `info.json` configuration of each Spec. ``` GET http://localhost:8080/api/specs ``` ### Parameters All parameters must be passed as JSON with request, except `id` that accepts regular param as well. List of available params: | Param | Value | Description | |---|---|---| | id | String | Get specific Spec information by ID - `base/btn`, `docs/spec` ([example](/api/specs?id=docs/spec)). | | cats | Boolean | Set to true, if you want to get categories info either. | | filter | Object | Return list of items by filter (filter something that we want to see). | | filterOut | Object | Return list of items by filter (filter something that we DON'T want to see). | Possible combinations: ``` [cats], [id|filter|filterOut], [filter|filterOut] ``` ### Filters These parameters are equal for `filter` and `filterOut` objects: | Param | Value | Description | |---|---|---| | filter.cats, filterOut.cats | Array with strings | Filter by Spec categories. | | filter.fields, filterOut.fields | Array with strings | Filter by existing files in `info.json`. | | filter.tags, filterOut.tags | Array with strings | Filter by specified tags in `info.json`. | And another custom param for filter: | Param | Value | Description | |---|---|---| | filter.forceTags | Array with strings | Filter by specified tags in `info.json`. | `filter.forceTags` has priority over `filterOut.tags`, if you want to force get all specs having specific tags. ### Raw Get raw, nested Navigation tree: ``` GET http://localhost:8080/api/specs/raw ``` ### Examples Return all specs that has info field: ``` $ curl -H "Content-Type: application/json" -X GET -d '{"filter":{"fields":["info"]}}' http://localhost:8080/api/specs ``` Return all specs except those, which have info field: ``` $ curl -H "Content-Type: application/json" -X GET -d '{"filterOut":{"fields":["info"]}}' http://localhost:8080/api/specs ``` ## GET HTML [Get](/api/specs/html) access to Spec examples HTML content. ``` GET http://localhost:8080/api/specs/html ``` ### Parameters | Param | Value | Description | |---|---|---| | id | String | Get specific Spec examples HTML information by ID - `base/btn`, `docs/spec` ([example](/api/specs/html?id=docs/spec)). | ## POST HTML ``` POST http://localhost:8080/api/specs/html ``` ### Parameters Params must be passed as JSON. List of possible params: | Param | Value | Description | |---|---|---| | data | Object | Data to post, will be extended on existing data. | | unflatten | Boolean | Set true, to unflatten object from `some/spec` before extending on current data. | #### Data example Data object must contain spec ID and `specFile` pointer, to follow current API storage structure. By default API expects to get nested object `docs.api.rest-api.specFile`, but you can also define a flat ID `docs/api/rest-api/specFile`, passing `unflatten:true` together with post request. ```js { "docs/api/rest-api/specFile": { "headResources": {}, "bodyResources": {}, "contents": [] } } ``` ## DELETE HTML ``` DELETE http://localhost:8080/api/specs/html ``` ### Parameters | Param | Value | Description | |---|---|---| | id | String | Spec ID (`some/spec`), that will be deleted from current data.| ================================================ FILE: docs/auth/info.json ================================================ { "keywords": "auth, github", "title": "Using Github Auth", "template": "doc" } ================================================ FILE: docs/auth/readme.md ================================================ ## Quick Start Before you start, please make sure that you have referenced GitHub App registered. Please visit [this link](https://developer.github.com/guides/basics-of-authentication/#registering-your-app) to get more information. If you allready have registered app replace existed github api key and secret into your local options file, as mentioned [here](/docs/base/#5!). Values, given below, are created as a demo. They are usable for [local instance](http://127.0.0.1:8080) at `127.0.0.1:8080` ```js github: { appId: "cf00a9e7ee5d9d6af36f", appSecret: "aebe08e0aa66f6911e4f54df81ce64c9d6e0003b" } ``` Please take into account, that auth feature is turned off by default and you have to set related option: `options.modulesEnabled.auth` to `true` in your config. After that, auth feature is able to use. ### Client-side auth control GitHub login module should be embedded into your `header.inc.html` or any other place you want. Here is the example: ```html ``` Auth uses `js-hook` and `source_login` classes for targeting. This hook is defined into `/assets/js/enter-the-source.js` file. ## Auth configuration Auth configuration is available from your instance [options](/docs/base/#configuration), as it was mentioned above. Here's few of available options: ```js // localStorage key for client-side user object 'storageKey': 'sourcejsUser', // avatar stub URL 'defaultAvatarURL': '/source/assets/i/unknown.gif', // set of client-side control classes 'classes': { 'controlsWrapper': 'source_login', 'loginButton': 'source_login-button', 'avatar': 'source_login-avatar', 'anonymous': 'anonymous', 'hook': 'js-hook' }, // login/logout button labels 'labels': { 'login': 'Login', 'logout': 'Logout' } ``` View the full list in `/assets/js/modules/auth.js` file. ## Auth modules usage Both server-side and client-side auth parts are able to use from other modules. Server-side part provides [The everyauth](https://github.com/bnoguchi/everyauth) API whitch it is based on. Also there are methods either to get or to set GitHub user into temporary users storage. Client-side part allows to login/logout using Github, to check if user is logined and to get user data. ================================================ FILE: docs/base/info.json ================================================ { "title": "Main Engine Documentation", "template": "doc" } ================================================ FILE: docs/base/readme.md ================================================ This page contains main information about SourceJS engine and it's set-up. SourceJS documentation is rendered by the engine itself and is shipped together with each instance. ## Install Before you start, please make sure that [Git](http://git-scm.com/downloads) and [Node.js](http://nodejs.org/download/) are already installed on your system. Then install these NMP packages: ```html npm install -g yo generator-sourcejs ``` Having all dependencies in-place you will get a special `yo sourcejs` generator available for new instance initialization: ```html mkdir sourcejs && cd sourcejs yo sourcejs ``` To set-up a new engine instance chose the first option `Init SourceJS in this folder`. Generator also helps to bootstrap new Spec pages or plugins. ### From NPM [![npm version](https://badge.fury.io/js/sourcejs.svg)](http://badge.fury.io/js/sourcejs) To install SourceJS as a NPM package, first clone clean `user` configuration folder and then execute `npm install`. ```html git clone https://github.com/sourcejs/init.git -b npm my-sourcejs && cd my-sourcejs npm install sourcejs --save npm start ``` Starting from 0.6.0 we're planning to change official install path to one with NPM packages. Yeoman generator will be also replaced by [sourcejs-cli](https://github.com/sourcejs/sourcejs-cli) with commands like `run`, `install` (plugin), `init`, `create`. ### Installing On Windows If you're running Windows and have some issues with [JSDom](https://github.com/tmpvar/jsdom) dependencies compilation, please check this [topic](https://github.com/sourcejs/Source/issues/23). Alternatively with 0.5.6 we prepared a special build without JSDom, until it's full removal from core at 0.6.0. ```html npm install -g yo generator-sourcejs mkdir sourcejs && cd sourcejs yo sourcejs --branch 0.5.6-no-jsdom ``` Please note that Clarify feature is not available in `no-jsdom` version. If you had generator installed before, run `npm update -g generator-sourcejs` (v.0.4.2+ required). And installing same build from NPM: ```html git clone https://github.com/sourcejs/init.git -b npm my-sourcejs && cd my-sourcejs npm install sourcejs@0.5.6-no-jsdom --save npm start ``` ## Commands ### Run Installation wizard will offer to start SourceJS right after initialization. To run it manually, trigger this command in newly created folder with SourceJS app: ```html npm start ``` To set an alternative server port, pass `-- -p 8081` option. Other configuration arguments are described in the help section: ```html npm start -- -h ``` ### Build During the initial set-up, generator will build everything for you. To re-build the engine, run this command: ```html npm run build ``` It will trigger `npm i` and default build task for updating dependencies and building SourceJS assets. See the full list of [all build tasks available](/docs/build-tasks). ### Update For updating SourceJS to a newer version, just pull the latest changes and trigger build: ```html git pull && npm run build ``` ## Creating First Spec Specs are the main content files in SourceJS engine, in them you define all your component description and UI code for rendered examples. Originally we use `*.src.html` and `*.md` file templates with custom flavoured syntax. It is also possible to configure other technologies for writing Specs using plugins like [Jade](https://github.com/sourcejs/sourcejs-jade). We treat Spec files as an interface, you can construct Spec page in many ways following only few simple rules. Each Spec folder must contain `info.json` with it's meta information and SourceJS compliant markup file. As an essential markup, engine requires only few hooks like `.source_section`, `.source_example` to define content sections and the rest is plain semantic HTML. ### Spec Starting Template
      After initialization, you get `sourcejs/user` folder, which is the place for all your custom content. All new Specs and configuration of main engine must be done there.
      The starting template for new Spec pages can be found in `sourcejs/docs/starting` folder. Copy the contents to a new folder in `source/user/specs` and you'll be ready to write a new spec. Check the SourceJS Spec page documentation. ### Server-side Templating Engines As we mentioned before, it's easy to use other server-side templating engines like Jade, you only need to create a simple SourceJS middleware ([example](https://github.com/sourcejs/sourcejs-jade)) or process your sources with Grunt/Gulp. By default all files are pre-processed with [EJS](http://ejs.co/), so you're free to use custom EJS features in any spec page - like includes or even plain JavaScript: ```html <%- include('filename.html') %> <% if (info.title === 'Title') {% > Action! <% } %> ``` Read more about Spec page generation helpers. ### Client-side Templating Engines For client-side templating you don't need any magic, just link Mustache or any other JS library to your page and use it whenever you want. Remember, SourceJS Specs are a simple static pages, that are then enchanted with client-side scripts and internal APIs. ## Examples Main [project website](http://sourcejs.com) is based on SourceJS platform, as well as all documentation that you're surfing right now. Engine docs are both viewable on [GitHub](https://github.com/sourcejs/Source/tree/master/docs) and in SourceJS environment. Inspect [Sourcejs.com source code](https://github.com/sourcejs/Sourcejs.com) to get better understanding of the basic `source/user` folder contents with engine configuration. ### Bootstrap Bundle To show you how SourceJS based documentation pages could be configured, we prepared a [Bootstrap demo bundle](https://github.com/sourcejs/example-bootstrap-bundle). It represents a recommended way of structuring UI components, keeping all module related technologies in one place. We highly encourage you setting up Bootstrap bundle on your SourceJS instance, and use it for experimenting and demoing documentation pages. Pull Requests are very welcome, adding more examples to this bundle, you will help yourself and other users getting more insights on how SourceJS specs could be organized. Read our how-to articles, to get more information about the [SourceJS catalog set-up](https://github.com/sourcejs/blog-howto/tree/master/catalog-setup). ### Specs Showcase Highlighting the variety of different ways for organizing Spec pages we gathered another special bundle. View it's source code at [showcase repo](https://github.com/sourcejs/example-specs-showcase) and compare with [rendered result](http://sourcejs.com/specs/example-specs-showcase/). Showcase includes both native Specs examples, and ones that are rendered with plugins like [sourcejs-contrib-dss](http://github.com/sourcejs/sourcejs-contrib-dss) and [sourcejs-jade](http://github.com/sourcejs/sourcejs-jade). Also check the SourceJS Spec page documentation. ### Style Guide Driven Development To get more insights about recommended workflow within Style Guide platform check [this example bundle](https://www.youtube.com/watch?v=KeR8Qhgyb6M) together with short screencast.
      ## Configuration SourceJS engine is highly configurable and allows to override almost any options from your instance set-up using personal configuration file `sourcejs/user/options.js`. All default, and safe to change options are described in base configuration file `sourcejs/options.js`. With version 0.5.3 we also introduced context level options `sourcejs-options.js`, which allows to configure any catalog specifically for your needs. Read more about it, and other configuration capabilities in [Engine Configuration](/docs/configuration) doc. ## Plugins As a Style Guide Platform we focus on flexibility and ease of integration. All SourceJS core modules are easy to configure and replace with your customized version. Plugins are working in the same way as core modules, but are kept outside the main platform, allowing to separate specific features. Here is a list of available plugins: * [sourcejs-slm](https://github.com/venticco/sourcejs-slm) (new) * [sourcejs-md-react](https://github.com/mik01aj/sourcejs-md-react) (new) * [sourcejs-contrib-browser-sync](https://github.com/sourcejs/sourcejs-contrib-browser-sync) (new) * [sourcejs-react](https://github.com/szarouski/sourcejs-react) (new) * [sourcejs-contrib-dss](http://github.com/sourcejs/sourcejs-contrib-dss) (new) * [sourcejs-spec-status](https://github.com/sourcejs/sourcejs-spec-status) * [sourcejs-crowd-voice](https://github.com/sourcejs/sourcejs-crowd-voice) * [sourcejs-jade](https://github.com/sourcejs/sourcejs-jade) * [sourcejs-comments](https://github.com/sourcejs/sourcejs-comments) * [sourcejs-specs-linting](https://github.com/sourcejs/sourcejs-specs-linting) * [sourcejs-spec-dependencies](https://github.com/sourcejs/sourcejs-spec-dependencies) * [sourcejs-smiles](https://github.com/sourcejs/sourcejs-smiles) These modules are able to extend both front-end and back-end part of the engine. To install any of official plugin, just use `npm install` in your `sourcejs/user` folder (note that some of them needs additional dependencies like [MongoDB](http://www.mongodb.org/) or [CouchDB](http://couchdb.apache.org/)). Follow [this guide](/docs/api/plugins) to learn how to develop own plugins for SourceJS Platform. ================================================ FILE: docs/build-tasks/info.json ================================================ { "title": "Build Tasks", "template": "doc" } ================================================ FILE: docs/build-tasks/readme.md ================================================ Note that these commands are related only to SourceJS engine build and development cycle. For building specs and other project related content use own build tasks from `sourcejs/user` folder. ## Scripts All automation tasks are triggered through NPM scripts listed in `package.json`. Here's a list of most common commands: ```js $ npm run build $ npm start $ npm run watch $ npm run watch:css $ npm run lint $ npm test $ npm run test:unit $ npm run test:func ``` * `npm run build` - default build command for preparing SourceJS assets, used as a post-install hook and after plugin installation * `npm start` - runs the engine * `npm run watch` - runs engine sources watcher, used in core client-side modules development ================================================ FILE: docs/clarify/info.json ================================================ { "title": "Clarify - Spec Section Testing Environment", "template": "doc" } ================================================ FILE: docs/clarify/readme.md ================================================ SourceJS middleware, that allows to open separate documentation examples in custom or clean environment for component testing and development. ## General information Clarify is an [expressJS](http://expressjs.com/) middleware built into SourceJS engine. Easy configurable through URL parameters in Spec pages: ```html http://localhost:8080/docs/spec/?clarify=true§ions=1.1 ``` When enabled, Clarify uses [JSdom](https://github.com/tmpvar/jsdom) for getting specified sections and wrap them in pre-defined or user templates. If you're using SourceJS HTML API, Clarify can be configured to take data directly from API storage as well. Clarify page is enhanced with helper panel, where you can chose any option available: [image](/docs/spec/?clarify=true§ions=1.1) ## List of parameters | Param | Value | Default setting | Description | |---|---|---| | clarify | Boolean | false | Turn on clarify middleware. | | sections | 1, 1.1, 3 | empty | List of sections to show. If empty, lists all high level section examples. | | fromApi | Boolean | false | Set to `true`, if want to get rendered HTML from API in response. | | apiUpdate | Boolean | false | Set to `true`, if want to run HTML API update on spec, before getting results. Used only in combination with `fromApi` | | nojs | Boolean | false | Turn on and off JS injection from the Spec. | | tpl | template-name | default | Define EJS template name to render sections. Templates are defined in `core/views/clarify/` and `user/core/views/clarify/`, user templates overrides core. | To play around with available URL params, open [Clarify page](/docs/spec/?clarify=true§ions=1.1) and fill the helpers form below. ## Use cases Clarify is covering wide range of use cases: * Testing in old desktop browsers, which are not supported in SourceJS Specs * Testing on mobile, for faster Spec page loading * Focusing on single example during development * Isoleted page for automated testing * Responsive design testing * Fast transfer of code examples to templates ================================================ FILE: docs/configuration/info.json ================================================ { "title": "Engine Configuration", "keywords": "options, options.js, sourcejs-options, context", "template": "doc" } ================================================ FILE: docs/configuration/readme.md ================================================ SourceJS engine is highly configurable and allows to override almost any options for your special needs. ## Configuration Hierarchy Starting from default configuration to spec page overrides, it's possible to re-define options on any level, before it reaches execution phase. This is the priority list of all available configuration levels (starting from default): * `sourcejs/options.js` - default engine configuration file with examples and documented fields, located next to engine sources * `sourcejs/user/options.js` - user level settings, as well as other configuration layers, it follows the same structure as default engine options * `sourcejs/user/**/sourcejs-options.js` - context level settings allows to specify custom configuration per catalogue (have some limitations), supports inheritance for child specs and catalogs * `info.json` `sourcejs` field - spec context level options, similar to `sourcejs-options.js` but affects only current spec page Each configuration layer has the same options structure as default reference `sourcejs/options.js`. ### Context Options Following the options priority hierarchy, context options allows to define highest priority configuration per catalog and spec. `sourcejs-option.js` file could be placed into any catalog in `sourcejs/user` folder and will affect all nested items. In this example, `catalog1/sourcejs-options.js` will affect both `catalog1/component1`, `catalog1/component2` specs and it's children: ```html /user catalog1 component1 component2 sourcejs-options.js catalog2 ``` On each spec page call, engine gathers all the context configuration files up the tree and merges it on top of each other. The same process will happen with spec context level options in `info.json`, with only difference that it won't be shared with child items. Here's an example of local `info.json` configuration: ```js { "title": "Spec Title", "sourcejs": { "plugins": { "plugins": "options" } } } ``` Since not all the configuration is dynamic, comparing to user level options, context options allows to change only these groups: * `assets` * `plugins` * `rendering` Note that context options does not allow changing `core` setting, to override them use `sourcejs/user/options.js` level instead. This limitation is defined by the fact, that only user level options are activated during the application start. ## Patching core assets
      Patching is not recommended approach, it's supposed to be used only for edge cases, before your changes will be merged in SourceJS core.
      To extend core Front-end assets, that are stored in `sourcejs/assets` we provide a patching feature. To use your own version of any core assets, all you need is to place your new files to `sourcejs/user/core/assets` folder. Custom routing will handle the rest: ```html sourcejs/assets/js/modules/ntf.js < sourcejs/user/source/assets/js/modules/ntf.js sourcejs/assets/js/modules/css.js < sourcejs/user/source/assets/js/modules/css.js ``` In this example, on request for `localhost:8080/source/assets/js/modules/ntf.js` engine will provide version from `sourcejs/user/source/assets` folder, instead of version from core `sourcejs/assets`. ================================================ FILE: docs/data/bootstrap.css ================================================ /*! * Bootstrap v3.3.4 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background-color: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } h1 { margin: .67em 0; font-size: 2em; } mark { color: #000; background: #ff0; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -.5em; } sub { bottom: -.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 1em 40px; } hr { height: 0; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } pre { overflow: auto; } code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } button, input, optgroup, select, textarea { margin: 0; font: inherit; color: inherit; } button { overflow: visible; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; } button[disabled], html input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } input { line-height: normal; } input[type="checkbox"], input[type="radio"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } 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; } fieldset { padding: .35em .625em .75em; margin: 0 2px; border: 1px solid #c0c0c0; } legend { padding: 0; border: 0; } textarea { overflow: auto; } optgroup { font-weight: bold; } table { border-spacing: 0; border-collapse: collapse; } td, th { padding: 0; } /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print { *, *:before, *:after { color: #000 !important; text-shadow: none !important; background: transparent !important; -webkit-box-shadow: none !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="#"]:after, a[href^="javascript:"]: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; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } select { background: #fff !important; } .navbar { display: none; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table td, .table th { background-color: #fff !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .glyphicon-asterisk:before { content: "\2a"; } .glyphicon-plus:before { content: "\2b"; } .glyphicon-euro:before, .glyphicon-eur:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-lock:before { content: "\e033"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-bookmark:before { content: "\e044"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-camera:before { content: "\e046"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-fire:before { content: "\e104"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-calendar:before { content: "\e109"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-bell:before { content: "\e123"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-wrench:before { content: "\e136"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-briefcase:before { content: "\e139"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-paperclip:before { content: "\e142"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-pushpin:before { content: "\e146"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } .glyphicon-cd:before { content: "\e201"; } .glyphicon-save-file:before { content: "\e202"; } .glyphicon-open-file:before { content: "\e203"; } .glyphicon-level-up:before { content: "\e204"; } .glyphicon-copy:before { content: "\e205"; } .glyphicon-paste:before { content: "\e206"; } .glyphicon-alert:before { content: "\e209"; } .glyphicon-equalizer:before { content: "\e210"; } .glyphicon-king:before { content: "\e211"; } .glyphicon-queen:before { content: "\e212"; } .glyphicon-pawn:before { content: "\e213"; } .glyphicon-bishop:before { content: "\e214"; } .glyphicon-knight:before { content: "\e215"; } .glyphicon-baby-formula:before { content: "\e216"; } .glyphicon-tent:before { content: "\26fa"; } .glyphicon-blackboard:before { content: "\e218"; } .glyphicon-bed:before { content: "\e219"; } .glyphicon-apple:before { content: "\f8ff"; } .glyphicon-erase:before { content: "\e221"; } .glyphicon-hourglass:before { content: "\231b"; } .glyphicon-lamp:before { content: "\e223"; } .glyphicon-duplicate:before { content: "\e224"; } .glyphicon-piggy-bank:before { content: "\e225"; } .glyphicon-scissors:before { content: "\e226"; } .glyphicon-bitcoin:before { content: "\e227"; } .glyphicon-btc:before { content: "\e227"; } .glyphicon-xbt:before { content: "\e227"; } .glyphicon-yen:before { content: "\00a5"; } .glyphicon-jpy:before { content: "\00a5"; } .glyphicon-ruble:before { content: "\20bd"; } .glyphicon-rub:before { content: "\20bd"; } .glyphicon-scale:before { content: "\e230"; } .glyphicon-ice-lolly:before { content: "\e231"; } .glyphicon-ice-lolly-tasted:before { content: "\e232"; } .glyphicon-education:before { content: "\e233"; } .glyphicon-option-horizontal:before { content: "\e234"; } .glyphicon-option-vertical:before { content: "\e235"; } .glyphicon-menu-hamburger:before { content: "\e236"; } .glyphicon-modal-window:before { content: "\e237"; } .glyphicon-oil:before { content: "\e238"; } .glyphicon-grain:before { content: "\e239"; } .glyphicon-sunglasses:before { content: "\e240"; } .glyphicon-text-size:before { content: "\e241"; } .glyphicon-text-color:before { content: "\e242"; } .glyphicon-text-background:before { content: "\e243"; } .glyphicon-object-align-top:before { content: "\e244"; } .glyphicon-object-align-bottom:before { content: "\e245"; } .glyphicon-object-align-horizontal:before { content: "\e246"; } .glyphicon-object-align-left:before { content: "\e247"; } .glyphicon-object-align-vertical:before { content: "\e248"; } .glyphicon-object-align-right:before { content: "\e249"; } .glyphicon-triangle-right:before { content: "\e250"; } .glyphicon-triangle-left:before { content: "\e251"; } .glyphicon-triangle-bottom:before { content: "\e252"; } .glyphicon-triangle-top:before { content: "\e253"; } .glyphicon-console:before { content: "\e254"; } .glyphicon-superscript:before { content: "\e255"; } .glyphicon-subscript:before { content: "\e256"; } .glyphicon-menu-left:before { content: "\e257"; } .glyphicon-menu-right:before { content: "\e258"; } .glyphicon-menu-down:before { content: "\e259"; } .glyphicon-menu-up:before { content: "\e260"; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.42857143; color: #333; background-color: #fff; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #337ab7; text-decoration: none; } a:hover, a:focus { color: #23527c; text-decoration: underline; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } figure { margin: 0; } img { vertical-align: middle; } .img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; max-width: 100%; height: auto; } .img-rounded { border-radius: 6px; } .img-thumbnail { display: inline-block; max-width: 100%; height: auto; padding: 4px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: all .2s ease-in-out; -o-transition: all .2s ease-in-out; transition: all .2s ease-in-out; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eee; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } [role="button"] { cursor: pointer; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, 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: #777; } h1, .h1, h2, .h2, h3, .h3 { margin-top: 20px; margin-bottom: 10px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { font-size: 65%; } h4, .h4, h5, .h5, h6, .h6 { margin-top: 10px; margin-bottom: 10px; } h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { font-size: 75%; } h1, .h1 { font-size: 36px; } h2, .h2 { font-size: 30px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16px; font-weight: 300; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small, .small { font-size: 85%; } mark, .mark { padding: .2em; background-color: #fcf8e3; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .text-justify { text-align: justify; } .text-nowrap { white-space: nowrap; } .text-lowercase { text-transform: lowercase; } .text-uppercase { text-transform: uppercase; } .text-capitalize { text-transform: capitalize; } .text-muted { color: #777; } .text-primary { color: #337ab7; } a.text-primary:hover { color: #286090; } .text-success { color: #3c763d; } a.text-success:hover { color: #2b542c; } .text-info { color: #31708f; } a.text-info:hover { color: #245269; } .text-warning { color: #8a6d3b; } a.text-warning:hover { color: #66512c; } .text-danger { color: #a94442; } a.text-danger:hover { color: #843534; } .bg-primary { color: #fff; background-color: #337ab7; } a.bg-primary:hover { background-color: #286090; } .bg-success { background-color: #dff0d8; } a.bg-success:hover { background-color: #c1e2b3; } .bg-info { background-color: #d9edf7; } a.bg-info:hover { background-color: #afd9ee; } .bg-warning { background-color: #fcf8e3; } a.bg-warning:hover { background-color: #f7ecb5; } .bg-danger { background-color: #f2dede; } a.bg-danger:hover { background-color: #e4b9b9; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eee; } 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; margin-left: -5px; list-style: none; } .list-inline > li { display: inline-block; padding-right: 5px; padding-left: 5px; } dl { margin-top: 0; margin-bottom: 20px; } dt, dd { line-height: 1.42857143; } 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; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #777; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; font-size: 17.5px; border-left: 5px solid #eee; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } blockquote footer, blockquote small, blockquote .small { display: block; font-size: 80%; line-height: 1.42857143; color: #777; } blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; text-align: right; border-right: 5px solid #eee; border-left: 0; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } address { margin-bottom: 20px; font-style: normal; line-height: 1.42857143; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 4px; } kbd { padding: 2px 4px; font-size: 90%; color: #fff; background-color: #333; border-radius: 3px; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); } kbd kbd { padding: 0; font-size: 100%; font-weight: bold; -webkit-box-shadow: none; box-shadow: none; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.42857143; color: #333; word-break: break-all; word-wrap: break-word; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 4px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } @media (min-width: 768px) { .container { width: 750px; } } @media (min-width: 992px) { .container { width: 970px; } } @media (min-width: 1200px) { .container { width: 1170px; } } .container-fluid { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } .row { margin-right: -15px; margin-left: -15px; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .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, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666667%; } .col-xs-10 { width: 83.33333333%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666667%; } .col-xs-7 { width: 58.33333333%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666667%; } .col-xs-4 { width: 33.33333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.66666667%; } .col-xs-1 { width: 8.33333333%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666667%; } .col-xs-pull-10 { right: 83.33333333%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666667%; } .col-xs-pull-7 { right: 58.33333333%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666667%; } .col-xs-pull-4 { right: 33.33333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.66666667%; } .col-xs-pull-1 { right: 8.33333333%; } .col-xs-pull-0 { right: auto; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666667%; } .col-xs-push-10 { left: 83.33333333%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666667%; } .col-xs-push-7 { left: 58.33333333%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666667%; } .col-xs-push-4 { left: 33.33333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.66666667%; } .col-xs-push-1 { left: 8.33333333%; } .col-xs-push-0 { left: auto; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666667%; } .col-xs-offset-10 { margin-left: 83.33333333%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666667%; } .col-xs-offset-7 { margin-left: 58.33333333%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666667%; } .col-xs-offset-4 { margin-left: 33.33333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.66666667%; } .col-xs-offset-1 { margin-left: 8.33333333%; } .col-xs-offset-0 { margin-left: 0; } @media (min-width: 768px) { .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 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666667%; } .col-sm-10 { width: 83.33333333%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666667%; } .col-sm-7 { width: 58.33333333%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666667%; } .col-sm-4 { width: 33.33333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.66666667%; } .col-sm-1 { width: 8.33333333%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666667%; } .col-sm-pull-10 { right: 83.33333333%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666667%; } .col-sm-pull-7 { right: 58.33333333%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666667%; } .col-sm-pull-4 { right: 33.33333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.66666667%; } .col-sm-pull-1 { right: 8.33333333%; } .col-sm-pull-0 { right: auto; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666667%; } .col-sm-push-10 { left: 83.33333333%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666667%; } .col-sm-push-7 { left: 58.33333333%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666667%; } .col-sm-push-4 { left: 33.33333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.66666667%; } .col-sm-push-1 { left: 8.33333333%; } .col-sm-push-0 { left: auto; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666667%; } .col-sm-offset-10 { margin-left: 83.33333333%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666667%; } .col-sm-offset-7 { margin-left: 58.33333333%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666667%; } .col-sm-offset-4 { margin-left: 33.33333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.66666667%; } .col-sm-offset-1 { margin-left: 8.33333333%; } .col-sm-offset-0 { margin-left: 0; } } @media (min-width: 992px) { .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 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666667%; } .col-md-10 { width: 83.33333333%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666667%; } .col-md-7 { width: 58.33333333%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666667%; } .col-md-4 { width: 33.33333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.66666667%; } .col-md-1 { width: 8.33333333%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666667%; } .col-md-pull-10 { right: 83.33333333%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666667%; } .col-md-pull-7 { right: 58.33333333%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666667%; } .col-md-pull-4 { right: 33.33333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.66666667%; } .col-md-pull-1 { right: 8.33333333%; } .col-md-pull-0 { right: auto; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666667%; } .col-md-push-10 { left: 83.33333333%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666667%; } .col-md-push-7 { left: 58.33333333%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666667%; } .col-md-push-4 { left: 33.33333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.66666667%; } .col-md-push-1 { left: 8.33333333%; } .col-md-push-0 { left: auto; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666667%; } .col-md-offset-10 { margin-left: 83.33333333%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666667%; } .col-md-offset-7 { margin-left: 58.33333333%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666667%; } .col-md-offset-4 { margin-left: 33.33333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.66666667%; } .col-md-offset-1 { margin-left: 8.33333333%; } .col-md-offset-0 { margin-left: 0; } } @media (min-width: 1200px) { .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 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666667%; } .col-lg-10 { width: 83.33333333%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666667%; } .col-lg-7 { width: 58.33333333%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666667%; } .col-lg-4 { width: 33.33333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.66666667%; } .col-lg-1 { width: 8.33333333%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666667%; } .col-lg-pull-10 { right: 83.33333333%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666667%; } .col-lg-pull-7 { right: 58.33333333%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666667%; } .col-lg-pull-4 { right: 33.33333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.66666667%; } .col-lg-pull-1 { right: 8.33333333%; } .col-lg-pull-0 { right: auto; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666667%; } .col-lg-push-10 { left: 83.33333333%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666667%; } .col-lg-push-7 { left: 58.33333333%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666667%; } .col-lg-push-4 { left: 33.33333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.66666667%; } .col-lg-push-1 { left: 8.33333333%; } .col-lg-push-0 { left: auto; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666667%; } .col-lg-offset-10 { margin-left: 83.33333333%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666667%; } .col-lg-offset-7 { margin-left: 58.33333333%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666667%; } .col-lg-offset-4 { margin-left: 33.33333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.66666667%; } .col-lg-offset-1 { margin-left: 8.33333333%; } .col-lg-offset-0 { margin-left: 0; } } table { background-color: transparent; } caption { padding-top: 8px; padding-bottom: 8px; color: #777; text-align: left; } th { text-align: left; } .table { width: 100%; max-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.42857143; vertical-align: top; border-top: 1px solid #ddd; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #ddd; } .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 #ddd; } .table .table { background-color: #fff; } .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 #ddd; } .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 #ddd; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-of-type(odd) { background-color: #f9f9f9; } .table-hover > tbody > tr:hover { background-color: #f5f5f5; } table col[class*="col-"] { position: static; display: table-column; float: none; } table td[class*="col-"], table th[class*="col-"] { position: static; 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-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { background-color: #e8e8e8; } .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; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { background-color: #d0e9c6; } .table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { background-color: #d9edf7; } .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { background-color: #c4e3f3; } .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; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { background-color: #faf2cc; } .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; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { background-color: #ebcccc; } .table-responsive { min-height: .01%; overflow-x: auto; } @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 15px; overflow-y: hidden; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #ddd; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; 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; line-height: normal; } input[type="file"] { display: block; } input[type="range"] { display: block; width: 100%; } select[multiple], select[size] { height: auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } output { display: block; padding-top: 7px; font-size: 14px; line-height: 1.42857143; color: #555; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); } .form-control::-moz-placeholder { color: #999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999; } .form-control::-webkit-input-placeholder { color: #999; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { background-color: #eee; opacity: 1; } .form-control[disabled], fieldset[disabled] .form-control { cursor: not-allowed; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { line-height: 34px; } input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm, .input-group-sm input[type="date"], .input-group-sm input[type="time"], .input-group-sm input[type="datetime-local"], .input-group-sm input[type="month"] { line-height: 30px; } input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg, .input-group-lg input[type="date"], .input-group-lg input[type="time"], .input-group-lg input[type="datetime-local"], .input-group-lg input[type="month"] { line-height: 46px; } } .form-group { margin-bottom: 15px; } .radio, .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { min-height: 20px; padding-left: 20px; 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"] { position: absolute; margin-top: 4px \9; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { position: relative; 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], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { cursor: not-allowed; } .radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { cursor: not-allowed; } .form-control-static { min-height: 34px; padding-top: 7px; padding-bottom: 7px; margin-bottom: 0; } .form-control-static.input-lg, .form-control-static.input-sm { padding-right: 0; padding-left: 0; } .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, select[multiple].input-sm { height: auto; } .form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.form-group-sm .form-control { height: 30px; line-height: 30px; } textarea.form-group-sm .form-control, select[multiple].form-group-sm .form-control { height: auto; } .form-group-sm .form-control-static { height: 30px; min-height: 32px; padding: 5px 10px; font-size: 12px; line-height: 1.5; } .input-lg { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } select.input-lg { height: 46px; line-height: 46px; } textarea.input-lg, select[multiple].input-lg { height: auto; } .form-group-lg .form-control { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } select.form-group-lg .form-control { height: 46px; line-height: 46px; } textarea.form-group-lg .form-control, select[multiple].form-group-lg .form-control { height: auto; } .form-group-lg .form-control-static { height: 46px; min-height: 38px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; } .has-feedback { position: relative; } .has-feedback .form-control { padding-right: 42.5px; } .form-control-feedback { position: absolute; top: 0; right: 0; z-index: 2; display: block; width: 34px; height: 34px; line-height: 34px; text-align: center; pointer-events: none; } .input-lg + .form-control-feedback { width: 46px; height: 46px; line-height: 46px; } .input-sm + .form-control-feedback { width: 30px; height: 30px; line-height: 30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label { color: #3c763d; } .has-success .form-control { border-color: #3c763d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-success .form-control:focus { border-color: #2b542c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; } .has-success .input-group-addon { color: #3c763d; background-color: #dff0d8; border-color: #3c763d; } .has-success .form-control-feedback { color: #3c763d; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label { color: #8a6d3b; } .has-warning .form-control { border-color: #8a6d3b; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-warning .form-control:focus { border-color: #66512c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; } .has-warning .input-group-addon { color: #8a6d3b; background-color: #fcf8e3; border-color: #8a6d3b; } .has-warning .form-control-feedback { color: #8a6d3b; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label { color: #a94442; } .has-error .form-control { border-color: #a94442; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-error .form-control:focus { border-color: #843534; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; } .has-error .input-group-addon { color: #a94442; background-color: #f2dede; border-color: #a94442; } .has-error .form-control-feedback { color: #a94442; } .has-feedback label ~ .form-control-feedback { top: 25px; } .has-feedback label.sr-only ~ .form-control-feedback { top: 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; width: auto; vertical-align: middle; } .form-inline .form-control-static { display: inline-block; } .form-inline .input-group { display: inline-table; vertical-align: middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { width: auto; } .form-inline .input-group > .form-control { width: 100%; } .form-inline .control-label { margin-bottom: 0; vertical-align: middle; } .form-inline .radio, .form-inline .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .form-inline .radio label, .form-inline .checkbox label { padding-left: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .form-inline .has-feedback .form-control-feedback { top: 0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { padding-top: 7px; margin-top: 0; margin-bottom: 0; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 27px; } .form-horizontal .form-group { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { padding-top: 7px; margin-bottom: 0; text-align: right; } } .form-horizontal .has-feedback .form-control-feedback { right: 15px; } @media (min-width: 768px) { .form-horizontal .form-group-lg .control-label { padding-top: 14.333333px; } } @media (min-width: 768px) { .form-horizontal .form-group-sm .control-label { padding-top: 6px; } } .btn { display: inline-block; padding: 6px 12px; margin-bottom: 0; font-size: 14px; font-weight: normal; line-height: 1.42857143; text-align: center; white-space: nowrap; vertical-align: middle; -ms-touch-action: manipulation; touch-action: manipulation; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background-image: none; border: 1px solid transparent; border-radius: 4px; } .btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus, .btn.focus { color: #333; text-decoration: none; } .btn:active, .btn.active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { pointer-events: none; cursor: not-allowed; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; opacity: .65; } .btn-default { color: #333; background-color: #fff; border-color: #ccc; } .btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { color: #333; background-color: #e6e6e6; 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.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: #fff; border-color: #ccc; } .btn-default .badge { color: #fff; background-color: #333; } .btn-primary { color: #fff; background-color: #337ab7; border-color: #2e6da4; } .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #fff; background-color: #286090; border-color: #204d74; } .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.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: #337ab7; border-color: #2e6da4; } .btn-primary .badge { color: #337ab7; background-color: #fff; } .btn-success { color: #fff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:hover, .btn-success:focus, .btn-success.focus, .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { color: #fff; background-color: #449d44; 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.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-success .badge { color: #5cb85c; background-color: #fff; } .btn-info { color: #fff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:hover, .btn-info:focus, .btn-info.focus, .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { color: #fff; background-color: #31b0d5; 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.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-info .badge { color: #5bc0de; background-color: #fff; } .btn-warning { color: #fff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:hover, .btn-warning:focus, .btn-warning.focus, .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { color: #fff; background-color: #ec971f; 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.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-warning .badge { color: #f0ad4e; background-color: #fff; } .btn-danger { color: #fff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:hover, .btn-danger:focus, .btn-danger.focus, .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { color: #fff; background-color: #c9302c; 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.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-danger .badge { color: #d9534f; background-color: #fff; } .btn-link { font-weight: normal; color: #337ab7; border-radius: 0; } .btn-link, .btn-link:active, .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: #23527c; 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: #777; text-decoration: none; } .btn-lg, .btn-group-lg > .btn { padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px; } .btn-sm, .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs, .btn-group-xs > .btn { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-block { display: block; width: 100%; } .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 .15s linear; -o-transition: opacity .15s linear; transition: opacity .15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } tr.collapse.in { display: table-row; } tbody.collapse.in { display: table-row-group; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition-timing-function: ease; -o-transition-timing-function: ease; transition-timing-function: ease; -webkit-transition-duration: .35s; -o-transition-duration: .35s; transition-duration: .35s; -webkit-transition-property: height, visibility; -o-transition-property: height, visibility; transition-property: height, visibility; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px dashed; border-right: 4px solid transparent; border-left: 4px solid transparent; } .dropup, .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; text-align: left; list-style: none; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); box-shadow: 0 6px 12px rgba(0, 0, 0, .175); } .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.42857143; color: #333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { color: #262626; text-decoration: none; background-color: #f5f5f5; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #fff; text-decoration: none; background-color: #337ab7; outline: 0; } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #777; } .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-menu-right { right: 0; left: auto; } .dropdown-menu-left { right: auto; left: 0; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.42857143; color: #777; white-space: nowrap; } .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 { content: ""; border-top: 0; border-bottom: 4px solid; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 2px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { right: 0; left: auto; } .navbar-right .dropdown-menu-left { right: auto; left: 0; } } .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 + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar { margin-left: -5px; } .btn-toolbar .btn-group, .btn-toolbar .input-group { float: left; } .btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-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-top-left-radius: 0; border-bottom-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:not(:last-child) > .btn:last-child, .btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-left-radius: 0; border-bottom-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .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, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn-group.open .dropdown-toggle.btn-link { -webkit-box-shadow: none; box-shadow: none; } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } .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-left-radius: 0; border-top-right-radius: 0; border-bottom-left-radius: 4px; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-left-radius: 0; border-top-right-radius: 0; } .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { display: table-cell; float: none; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } .btn-group-justified > .btn-group .dropdown-menu { left: auto; } [data-toggle="buttons"] > .btn input[type="radio"], [data-toggle="buttons"] > .btn-group > .btn input[type="radio"], [data-toggle="buttons"] > .btn input[type="checkbox"], [data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group[class*="col-"] { float: none; padding-right: 0; padding-left: 0; } .input-group .form-control { position: relative; z-index: 2; float: left; width: 100%; margin-bottom: 0; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.3333333; 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: 46px; line-height: 46px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].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, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].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; color: #555; text-align: center; background-color: #eee; border: 1px solid #ccc; 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 > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { 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 > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; font-size: 0; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -1px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { z-index: 2; } .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { margin-right: -1px; } .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { margin-left: -1px; } .nav { padding-left: 0; margin-bottom: 0; list-style: none; } .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: #eee; } .nav > li.disabled > a { color: #777; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #777; text-decoration: none; cursor: not-allowed; background-color: transparent; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eee; border-color: #337ab7; } .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 #ddd; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.42857143; border: 1px solid transparent; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eee #eee #ddd; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555; cursor: default; background-color: #fff; border: 1px solid #ddd; border-bottom-color: transparent; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-tabs.nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } .nav-tabs.nav-justified > li > a { margin-bottom: 0; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #ddd; } @media (min-width: 768px) { .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #fff; } } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 4px; } .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: #fff; background-color: #337ab7; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } .nav-justified > li > a { margin-bottom: 0; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border: 1px solid #ddd; } @media (min-width: 768px) { .nav-tabs-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border-bottom-color: #fff; } } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-left-radius: 0; border-top-right-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 20px; border: 1px solid transparent; } @media (min-width: 768px) { .navbar { border-radius: 4px; } } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { padding-right: 15px; padding-left: 15px; overflow-x: visible; -webkit-overflow-scrolling: touch; border-top: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; -webkit-box-shadow: none; 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-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { padding-right: 0; padding-left: 0; } } .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 340px; } @media (max-device-width: 480px) and (orientation: landscape) { .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 200px; } } .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { z-index: 1000; 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; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; border-width: 1px 0 0; } .navbar-brand { float: left; height: 50px; padding: 15px 15px; font-size: 18px; line-height: 20px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } .navbar-brand > img { display: block; } @media (min-width: 768px) { .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; padding: 9px 10px; margin-top: 8px; margin-right: 15px; margin-bottom: 8px; background-color: transparent; background-image: none; border: 1px solid transparent; border-radius: 4px; } .navbar-toggle:focus { outline: 0; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 7.5px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 20px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; -webkit-box-shadow: none; 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; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 15px; padding-bottom: 15px; } } .navbar-form { padding: 10px 15px; margin-top: 8px; margin-right: -15px; margin-bottom: 8px; margin-left: -15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .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; width: auto; vertical-align: middle; } .navbar-form .form-control-static { display: inline-block; } .navbar-form .input-group { display: inline-table; vertical-align: middle; } .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn, .navbar-form .input-group .form-control { width: auto; } .navbar-form .input-group > .form-control { width: 100%; } .navbar-form .control-label { margin-bottom: 0; vertical-align: middle; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .navbar-form .radio label, .navbar-form .checkbox label { padding-left: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .navbar-form .has-feedback .form-control-feedback { top: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } .navbar-form .form-group:last-child { margin-bottom: 0; } } @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-left-radius: 0; border-top-right-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { margin-bottom: 0; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-btn { margin-top: 8px; margin-bottom: 8px; } .navbar-btn.btn-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-btn.btn-xs { margin-top: 14px; margin-bottom: 14px; } .navbar-text { margin-top: 15px; margin-bottom: 15px; } @media (min-width: 768px) { .navbar-text { float: left; margin-right: 15px; margin-left: 15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; margin-right: -15px; } .navbar-right ~ .navbar-right { margin-right: 0; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777; } .navbar-default .navbar-nav > li > a { color: #777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #ccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #ddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #ddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #888; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { color: #555; background-color: #e7e7e7; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #ccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777; } .navbar-default .navbar-link:hover { color: #333; } .navbar-default .btn-link { color: #777; } .navbar-default .btn-link:hover, .navbar-default .btn-link:focus { color: #333; } .navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:hover, .navbar-default .btn-link[disabled]:focus, fieldset[disabled] .navbar-default .btn-link:focus { color: #ccc; } .navbar-inverse { background-color: #222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #9d9d9d; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-text { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #fff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #fff; } .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: #fff; background-color: #080808; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #9d9d9d; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #fff; 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: #fff; 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: #444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #9d9d9d; } .navbar-inverse .navbar-link:hover { color: #fff; } .navbar-inverse .btn-link { color: #9d9d9d; } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { color: #fff; } .navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link[disabled]:focus, fieldset[disabled] .navbar-inverse .btn-link:focus { color: #444; } .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: #ccc; content: "/\00a0"; } .breadcrumb > .active { color: #777; } .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.42857143; color: #337ab7; text-decoration: none; background-color: #fff; border: 1px solid #ddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-top-left-radius: 4px; border-bottom-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 { color: #23527c; background-color: #eee; border-color: #ddd; } .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: #fff; cursor: default; background-color: #337ab7; border-color: #337ab7; } .pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #777; cursor: not-allowed; background-color: #fff; border-color: #ddd; } .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-top-left-radius: 6px; border-bottom-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-top-left-radius: 3px; border-bottom-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 li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #fff; border: 1px solid #ddd; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #eee; } .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: #777; cursor: not-allowed; background-color: #fff; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } a.label:hover, a.label:focus { color: #fff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #777; } .label-default[href]:hover, .label-default[href]:focus { background-color: #5e5e5e; } .label-primary { background-color: #337ab7; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #286090; } .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: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; background-color: #777; border-radius: 10px; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .btn-xs .badge, .btn-group-xs > .btn .badge { top: 0; padding: 1px 5px; } a.badge:hover, a.badge:focus { color: #fff; text-decoration: none; cursor: pointer; } .list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #337ab7; background-color: #fff; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding: 30px 15px; margin-bottom: 30px; color: inherit; background-color: #eee; } .jumbotron h1, .jumbotron .h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 21px; font-weight: 200; } .jumbotron > hr { border-top-color: #d5d5d5; } .container .jumbotron, .container-fluid .jumbotron { border-radius: 6px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding: 48px 0; } .container .jumbotron, .container-fluid .jumbotron { padding-right: 60px; padding-left: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 63px; } } .thumbnail { display: block; padding: 4px; margin-bottom: 20px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: border .2s ease-in-out; -o-transition: border .2s ease-in-out; transition: border .2s ease-in-out; } .thumbnail > img, .thumbnail a > img { margin-right: auto; margin-left: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #337ab7; } .thumbnail .caption { padding: 9px; color: #333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable, .alert-dismissible { padding-right: 35px; } .alert-dismissable .close, .alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #2b542c; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #245269; } .alert-warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .alert-warning hr { border-top-color: #f7e1b5; } .alert-warning .alert-link { color: #66512c; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .alert-danger hr { border-top-color: #e4b9c0; } .alert-danger .alert-link { color: #843534; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 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, .1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); } .progress-bar { float: left; width: 0; height: 100%; font-size: 12px; line-height: 20px; color: #fff; text-align: center; background-color: #337ab7; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); -webkit-transition: width .6s ease; -o-transition: width .6s ease; transition: width .6s ease; } .progress-striped .progress-bar, .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -webkit-background-size: 40px 40px; background-size: 40px 40px; } .progress.active .progress-bar, .progress-bar.active { -webkit-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-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #5bc0de; } .progress-striped .progress-bar-info { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #f0ad4e; } .progress-striped .progress-bar-warning { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #d9534f; } .progress-striped .progress-bar-danger { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media, .media-body { overflow: hidden; zoom: 1; } .media-body { width: 10000px; } .media-object { display: block; } .media-right, .media > .pull-right { padding-left: 10px; } .media-left, .media > .pull-left { padding-right: 10px; } .media-left, .media-right, .media-body { display: table-cell; vertical-align: top; } .media-middle { vertical-align: middle; } .media-bottom { vertical-align: bottom; } .media-heading { margin-top: 0; margin-bottom: 5px; } .media-list { padding-left: 0; list-style: none; } .list-group { padding-left: 0; margin-bottom: 20px; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #fff; border: 1px solid #ddd; } .list-group-item:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } a.list-group-item { color: #555; } a.list-group-item .list-group-item-heading { color: #333; } a.list-group-item:hover, a.list-group-item:focus { color: #555; text-decoration: none; background-color: #f5f5f5; } .list-group-item.disabled, .list-group-item.disabled:hover, .list-group-item.disabled:focus { color: #777; cursor: not-allowed; background-color: #eee; } .list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading { color: inherit; } .list-group-item.disabled .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text { color: #777; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #fff; background-color: #337ab7; border-color: #337ab7; } .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, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > .small { 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: #c7ddef; } .list-group-item-success { color: #3c763d; background-color: #dff0d8; } a.list-group-item-success { color: #3c763d; } a.list-group-item-success .list-group-item-heading { color: inherit; } a.list-group-item-success:hover, a.list-group-item-success:focus { color: #3c763d; background-color: #d0e9c6; } a.list-group-item-success.active, a.list-group-item-success.active:hover, a.list-group-item-success.active:focus { color: #fff; background-color: #3c763d; border-color: #3c763d; } .list-group-item-info { color: #31708f; background-color: #d9edf7; } a.list-group-item-info { color: #31708f; } a.list-group-item-info .list-group-item-heading { color: inherit; } a.list-group-item-info:hover, a.list-group-item-info:focus { color: #31708f; background-color: #c4e3f3; } a.list-group-item-info.active, a.list-group-item-info.active:hover, a.list-group-item-info.active:focus { color: #fff; background-color: #31708f; border-color: #31708f; } .list-group-item-warning { color: #8a6d3b; background-color: #fcf8e3; } a.list-group-item-warning { color: #8a6d3b; } a.list-group-item-warning .list-group-item-heading { color: inherit; } a.list-group-item-warning:hover, a.list-group-item-warning:focus { color: #8a6d3b; background-color: #faf2cc; } a.list-group-item-warning.active, a.list-group-item-warning.active:hover, a.list-group-item-warning.active:focus { color: #fff; background-color: #8a6d3b; border-color: #8a6d3b; } .list-group-item-danger { color: #a94442; background-color: #f2dede; } a.list-group-item-danger { color: #a94442; } a.list-group-item-danger .list-group-item-heading { color: inherit; } a.list-group-item-danger:hover, a.list-group-item-danger:focus { color: #a94442; background-color: #ebcccc; } a.list-group-item-danger.active, a.list-group-item-danger.active:hover, a.list-group-item-danger.active:focus { color: #fff; background-color: #a94442; border-color: #a94442; } .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: #fff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); box-shadow: 0 1px 1px rgba(0, 0, 0, .05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 16px; color: inherit; } .panel-title > a, .panel-title > small, .panel-title > .small, .panel-title > small > a, .panel-title > .small > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #ddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .list-group, .panel > .panel-collapse > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } .panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .list-group + .panel-footer { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { margin-bottom: 0; } .panel > .table caption, .panel > .table-responsive > .table caption, .panel > .panel-collapse > .table caption { padding-right: 15px; padding-left: 15px; } .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { border-top-left-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { border-top-right-radius: 3px; } .panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { border-bottom-right-radius: 3px; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { border-top: 1px solid #ddd; } .panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { border-bottom: 0; } .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { border-bottom: 0; } .panel > .table-responsive { margin-bottom: 0; border: 0; } .panel-group { margin-bottom: 20px; } .panel-group .panel { margin-bottom: 0; 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, .panel-group .panel-heading + .panel-collapse > .list-group { border-top: 1px solid #ddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #ddd; } .panel-default { border-color: #ddd; } .panel-default > .panel-heading { color: #333; background-color: #f5f5f5; border-color: #ddd; } .panel-default > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ddd; } .panel-default > .panel-heading .badge { color: #f5f5f5; background-color: #333; } .panel-default > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ddd; } .panel-primary { border-color: #337ab7; } .panel-primary > .panel-heading { color: #fff; background-color: #337ab7; border-color: #337ab7; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { border-top-color: #337ab7; } .panel-primary > .panel-heading .badge { color: #337ab7; background-color: #fff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #337ab7; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-heading .badge { color: #dff0d8; background-color: #3c763d; } .panel-success > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #d6e9c6; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse > .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-heading .badge { color: #d9edf7; background-color: #31708f; } .panel-info > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #bce8f1; } .panel-warning { border-color: #faebcc; } .panel-warning > .panel-heading { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .panel-warning > .panel-heading + .panel-collapse > .panel-body { border-top-color: #faebcc; } .panel-warning > .panel-heading .badge { color: #fcf8e3; background-color: #8a6d3b; } .panel-warning > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #faebcc; } .panel-danger { border-color: #ebccd1; } .panel-danger > .panel-heading { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .panel-danger > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ebccd1; } .panel-danger > .panel-heading .badge { color: #f2dede; background-color: #a94442; } .panel-danger > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ebccd1; } .embed-responsive { position: relative; display: block; height: 0; padding: 0; overflow: hidden; } .embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object, .embed-responsive video { position: absolute; top: 0; bottom: 0; left: 0; width: 100%; height: 100%; border: 0; } .embed-responsive-16by9 { padding-bottom: 56.25%; } .embed-responsive-4by3 { padding-bottom: 75%; } .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, .05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); } .well blockquote { border-color: #ddd; border-color: rgba(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: #000; text-shadow: 0 1px 0 #fff; filter: alpha(opacity=20); opacity: .2; } .close:hover, .close:focus { color: #000; text-decoration: none; cursor: pointer; filter: alpha(opacity=50); opacity: .5; } button.close { -webkit-appearance: none; padding: 0; cursor: pointer; background: transparent; border: 0; } .modal-open { overflow: hidden; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1050; display: none; overflow: hidden; -webkit-overflow-scrolling: touch; outline: 0; } .modal.fade .modal-dialog { -webkit-transition: -webkit-transform .3s ease-out; -o-transition: -o-transform .3s ease-out; transition: transform .3s ease-out; -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); -o-transform: translate(0, -25%); transform: translate(0, -25%); } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); -o-transform: translate(0, 0); transform: translate(0, 0); } .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .modal-dialog { position: relative; width: auto; margin: 10px; } .modal-content { position: relative; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #999; border: 1px solid rgba(0, 0, 0, .2); border-radius: 6px; outline: 0; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); box-shadow: 0 3px 9px rgba(0, 0, 0, .5); } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; background-color: #000; } .modal-backdrop.fade { filter: alpha(opacity=0); opacity: 0; } .modal-backdrop.in { filter: alpha(opacity=50); opacity: .5; } .modal-header { min-height: 16.42857143px; padding: 15px; border-bottom: 1px solid #e5e5e5; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: 1.42857143; } .modal-body { position: relative; padding: 15px; } .modal-footer { padding: 15px; text-align: right; border-top: 1px solid #e5e5e5; } .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; } .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } @media (min-width: 768px) { .modal-dialog { width: 600px; margin: 30px auto; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); box-shadow: 0 5px 15px rgba(0, 0, 0, .5); } .modal-sm { width: 300px; } } @media (min-width: 992px) { .modal-lg { width: 900px; } } .tooltip { position: absolute; z-index: 1070; display: block; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 1.4; filter: alpha(opacity=0); opacity: 0; } .tooltip.in { filter: alpha(opacity=90); opacity: .9; } .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: #fff; text-align: center; text-decoration: none; background-color: #000; 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-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-left .tooltip-arrow { right: 5px; bottom: 0; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-right .tooltip-arrow { bottom: 0; left: 5px; margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-width: 5px 5px 5px 0; border-right-color: #000; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-width: 5px 0 5px 5px; border-left-color: #000; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-left .tooltip-arrow { top: 0; right: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-right .tooltip-arrow { top: 0; left: 5px; margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .popover { position: absolute; top: 0; left: 0; z-index: 1060; display: none; max-width: 276px; padding: 1px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 1.42857143; text-align: left; white-space: normal; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .2); border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); box-shadow: 0 5px 10px rgba(0, 0, 0, .2); } .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; 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 { content: ""; border-width: 10px; } .popover.top > .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999; border-top-color: rgba(0, 0, 0, .25); border-bottom-width: 0; } .popover.top > .arrow:after { bottom: 1px; margin-left: -10px; content: " "; border-top-color: #fff; border-bottom-width: 0; } .popover.right > .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999; border-right-color: rgba(0, 0, 0, .25); border-left-width: 0; } .popover.right > .arrow:after { bottom: -10px; left: 1px; content: " "; border-right-color: #fff; border-left-width: 0; } .popover.bottom > .arrow { top: -11px; left: 50%; margin-left: -11px; border-top-width: 0; border-bottom-color: #999; border-bottom-color: rgba(0, 0, 0, .25); } .popover.bottom > .arrow:after { top: 1px; margin-left: -10px; content: " "; border-top-width: 0; border-bottom-color: #fff; } .popover.left > .arrow { top: 50%; right: -11px; margin-top: -11px; border-right-width: 0; border-left-color: #999; border-left-color: rgba(0, 0, 0, .25); } .popover.left > .arrow:after { right: 1px; bottom: -10px; content: " "; border-right-width: 0; border-left-color: #fff; } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: .6s ease-in-out left; -o-transition: .6s ease-in-out left; transition: .6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } @media all and (transform-3d), (-webkit-transform-3d) { .carousel-inner > .item { -webkit-transition: -webkit-transform .6s ease-in-out; -o-transition: -o-transform .6s ease-in-out; transition: transform .6s ease-in-out; -webkit-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000; perspective: 1000; } .carousel-inner > .item.next, .carousel-inner > .item.active.right { left: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } .carousel-inner > .item.prev, .carousel-inner > .item.active.left { left: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right, .carousel-inner > .item.active { left: 0; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .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: #fff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); filter: alpha(opacity=50); opacity: .5; } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); background-repeat: repeat-x; } .carousel-control.right { right: 0; left: auto; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); background-repeat: repeat-x; } .carousel-control:hover, .carousel-control:focus { color: #fff; text-decoration: none; filter: alpha(opacity=90); outline: 0; opacity: .9; } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; margin-top: -10px; font-family: serif; line-height: 1; } .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; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); border: 1px solid #fff; border-radius: 10px; } .carousel-indicators .active { width: 12px; height: 12px; margin: 0; background-color: #fff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #fff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -15px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -15px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .pager:before, .pager:after, .panel-body:before, .panel-body:after, .modal-footer:before, .modal-footer:after { display: table; content: " "; } .clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .btn-toolbar:after, .btn-group-vertical > .btn-group:after, .nav:after, .navbar:after, .navbar-header:after, .navbar-collapse:after, .pager:after, .panel-body:after, .modal-footer:after { clear: both; } .center-block { display: block; margin-right: auto; margin-left: auto; } .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; } .hidden { display: none !important; } .affix { position: fixed; } @-ms-viewport { width: device-width; } .visible-xs, .visible-sm, .visible-md, .visible-lg { display: none !important; } .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (max-width: 767px) { .visible-xs-block { display: block !important; } } @media (max-width: 767px) { .visible-xs-inline { display: inline !important; } } @media (max-width: 767px) { .visible-xs-inline-block { display: inline-block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-block { display: block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline { display: inline !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline-block { display: inline-block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-block { display: block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline { display: inline !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline-block { display: inline-block !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg-block { display: block !important; } } @media (min-width: 1200px) { .visible-lg-inline { display: inline !important; } } @media (min-width: 1200px) { .visible-lg-inline-block { display: inline-block !important; } } @media (max-width: 767px) { .hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } } .visible-print-block { display: none !important; } @media print { .visible-print-block { display: block !important; } } .visible-print-inline { display: none !important; } @media print { .visible-print-inline { display: inline !important; } } .visible-print-inline-block { display: none !important; } @media print { .visible-print-inline-block { display: inline-block !important; } } @media print { .hidden-print { display: none !important; } } /*# sourceMappingURL=bootstrap.css.map */ ================================================ FILE: docs/data-nav/example/info.json ================================================ { "title": "Example Title", "info": "Example description text from `info.json` `info` field." } ================================================ FILE: docs/data-nav/example/readme.md ================================================ # Some page With some text. ================================================ FILE: docs/data-nav/info.json ================================================ { "title": "Auto-generated Navigation", "template": "doc" } ================================================ FILE: docs/data-nav/readme.md ================================================ Navigation blocks in SourceJS are automatically generated on client-side, replacing a defined HTML hook with hand-crafted configuration. ## Embedding Copy and modify this HTML snippet to set-up your custom navigation. ```html <div class="source_catalog" data-nav="/docs"></div> ``` `data-nav` attribute defines the catalogue to be displayed. Inserting mentioned code in any SourceJS page a navigation like this will be generated:
      ### Absolute paths We recommend using absolute paths as most stable and predictable. ```html <div class="source_catalog" data-nav="/docs/api"></div> ``` Renders this navigation block:
      ### Relative paths Relative paths are also supported. During hook initialization, engine will replace `./` with current navigation URL. ```html <div class="source_catalog" data-nav="./api"></div> ``` Putting this HTML snippet in `localhost:8080/docs` page, it will be resolved to `/docs/api/` catalog, same as with absolute path. This feature is especially useful for nested catalogs and stand-alone collections like [example-specs-showcase](https://github.com/sourcejs/example-specs-showcase/blob/master/index.src.html). ## Headings and description automatic output Auto-generated title and description in navigation block are taked directly from `info.json` meta file. ```html <div class="source_catalog" data-nav="./example"></div> ```
      If there is no description, or you want to leave custom text, just use this extra markup: ```html <div class="source_catalog" data-nav="./example"> <h2>Custom Title</h2> <p>Custom description for catalogue.<p> </div> ```

      Custom Title

      Custom description for catalogue.

      ## Filtering by tag As it's possible to define different tags for specs in `info.json` [files](/docs/info-json/)), they can be used for filtering custom navigation collection. ```html <div class="source_catalog" data-nav="/docs" data-tag="templates"></div> ```

      Specs with "templates" tag

      To call all specs without a tags, you can filter by `"without-tag"` string. Multiple tags filtering is also supported - `templates, navigation`. ## Customizing Templates Navigation markup is created from templates. You can extend this templates using `options.js` and [Lodash](https://lodash.com) templates syntax: ```js module.exports = { core: { ... }, assets: { ... moduleOptions: { globalNav: { templates: { catalogHeader: _.template('

      <%= catalogMeta.title %>

      '), } } } } } ``` ================================================ FILE: docs/index.src.html ================================================

      Documentation and Examples

      Templates

      ================================================ FILE: docs/info-json/info.json ================================================ { "title": "Info.json - Spec Description", "keywords": "spec meta, template, specFile", "template": "doc" } ================================================ FILE: docs/info-json/readme.md ================================================ `info.json` file in spec folder is used for defining page meta information and local options set-up. Use it for customizing global navigation or with your custom fields. ## Used options List of used options in `info.json`: ```js { "title": "Info.json - Spec Description", "author": "Robert Haritonov", "tag": ["tags, for, search"], "info": "Spec description.", "role": "navigation", "template": ["custom" | "$(context)/template.ejs"], "specFile": "index.custom.hmtl", "thumbnailPath": "custom-thumbnail.png" "sourcejs": { "assets" :{} } } ``` * `title` - the only mandatory parameter, defines spec title in navigation * `author` - used in global navigation * `tag` - tags are used for search, filtering through [API](/docs/api) and [auto-generated navigation](/docs/data-nav) * `info` - spec description * `role` - set the page role ("navigation" or "spec"), used for setting different `*.src.html` files templates * `thumbnailPath` - custom relative path to spec page preview image Feel free to add any custom meta information to `info.json` file for your needs as well. This info will be then available from [Spec API](/docs/api), in middleware-plugins (`req.specData.info`) and for adding custom [ESJ templating](/docs/base/#server-side-templating-engines). ### template `template` field allows defining custom parent template for rendering the spec page. Available values: * `tpl-name` - `*.ejs` template in `user/core/views` folder * `$(context)/tpl-name.ejs` - path to template relative to current `info.json` location, other available placeholders - `$(sourcejs)`, `$(user)` ### specFile `specFile` - allows to define custom spec files source, overriding `options.rendering.specFiles` configuration. ### sourcejs `sourcejs` - this field accepts a local configuration object for configuring spec level context options. Read more about engine configuration capabilities in [Engine Configuration](/docs/configuration) doc. ================================================ FILE: docs/info.json ================================================ { "title": "Documentation and Examples", "role": "navigation", "info": "Every SourceJS instance is packed with own docs catalog." } ================================================ FILE: docs/markdown/info.json ================================================ { "title": "Markdown Support", "template": "doc" } ================================================ FILE: docs/markdown/readme.md ================================================ SourceJS supports markdown as pure `.md` files and renders it in any other extension files using `` HTML tag. ## Basics Engine treats `index.md` and `readme.md` as main Spec files. Readme file is taken with lowest priority after `index.src.html`, `index.md` and others. In case if Spec folder contains `index.src.html` and `index.md`, first one will have higher priority. ## Markup examples [Check out source code of this file](/docs/markdown/readme.md) and compare with this rendered Markdown page. * List 1 * List 2 1. Ordered list 1 2. Ordered list 2 | Table | Value | |---|---| | Td | value | ```html ```example code example ``` ``` ```example Mark code with `example` keyword to define `.source_example` with your rendered component view.

      Example button ``` ## Code examples SourceJS code blocks are aligned with standard Markdown style. List of demo code examples: ``` Some random code .css { color:red; } ``` ```css .css { color:red; } ``` ```js var test = function(){}; ``` ```html
      ``` ```example rendered example ``` ## h2 ```example code example in h2 ``` ### h3 Text ```example code example in h3 ``` * list * list #### h4 Text ```example code example in h4 ``` ================================================ FILE: docs/migration/info.json ================================================ { "title": "Migration Notes", "template": "doc" } ================================================ FILE: docs/migration/readme.md ================================================ Here you will find migration instructions from different Source versions ## From 0.4.0-beta to 0.4.0-rc * Update engine files, run `npm run build` in the root of engine * If you have custom `user/core/views`, then add `info.*` namespace to all data in template that is taken from `info.json` (`info.author`, `info.title`, `info.keywords`) * Set at your views and `*.html` spec files new path to main client-side file: `/source/assets/js/enter-the-source.js` ================================================ FILE: docs/spec/css/spec.css ================================================ /* Spec page block styles ---------------------------------------------------------------------------------- */ .some-code { font-size: 13px; } /* /Spec page block styles ---------------------------------------------------------------------------------- */ ================================================ FILE: docs/spec/index.src.html ================================================ 

      This page highlights the basic features of SourceJS documentation pages that we call Specs. To create your own Spec, follow this short guide.

      For text description, SourceJS uses basic HTML markup, and custom CSS class hooks. To render highlighted description block like this, add .source_info class to a any HTML element on page.

      Warning blocks can be added using .source_warn:

      It is highly recommended that you read the HTML source of this document.

      Some related useful information you can wrap with .source_doc

      Links can be highlighted as link to design specification by adding .source_a_d as a class on the link

      Links can be highlighted as link to other specification by adding .source_a_s as a class on the link

      Highlighting can be achieved by either wrapping text in <code> tags, or by using the .source_hl class.

      Spec Section Heading

      Links can be highlighted by adding .source_a_hl as a class on the link

      Notes specific to particular interface elements can be documented here. Heading numbering is generated dynamically, and is supported up to 3 levels deep.

      To render examples of interface elements, place them in a div with a class of .source_example:

      Hello world;

      Rendered code examples - main documentation blocks.

      Engine decorative styles do not affect this block. Link your project files and start prototyping.

      This page has Twitter Bootstrap styles linked as Master App Styles.

      Hey!

      Show source code

      You can fill custom source code blocks before "source_example". If <code class="src-html"> is not set, it will be created and filled automatically from "source_example" contents.

      Click "Show source" in the inner navigation menu on the right to reveal the source for all examples.

      To render and highlight the source code, wrap it in <code class="src-html">, <code class="src-css">
      or <code class="src-js"> tags, depending on the language you want to highlight, like this:

      <code class="src-html">
      Bar
      </code>
      <code class="src-css"> <style> .css_source { make-it: awesome; } </style> </code> <code class="src-js"> </code>

      By default (before 0.6.0) all code blocks are hidden by default, and are shown after toggling "Show source code" in menu. To define block as visible by default, add .source_visible class next to .src-*

      Linking Your Project's CSS

      Your project's CSS should be linked to each spec page to render your examples using /user/core/views/spec.ejs, or you can also add styles specific to a spec page:

      <link href="/docs/data/bootstrap.css" rel="stylesheet"> <link href="css/spec.css" rel="stylesheet">

      Spec Page Markup

      SourceJS Specs support styling of these semantic HTML tags, that you could use to describe your sections:

      • p
      • h1
      • h2
      • h3
      • h4
      • ol
      • ul
      • li
      • table
      • code

      This styling is not applied inside example blocks.

      From 0.5.0 SourceJS fully supports Markdown based Specs as well. You can use <markdown> tag in any Spec templates including index.src.html, or just use readme.md templates instead.

      Read more about markdown support

      Source code

      This spec uses index.src.html template, view it's source.

      ================================================ FILE: docs/spec/info.json ================================================ { "keywords": "create spec, spec markup", "template": "doc", "title": "Spec Page Documentation" } ================================================ FILE: docs/spec-helpers/examples/buttons.html ================================================

      <%- info.title %>

      <% var buttons = ['btn-group-lg', '', 'btn-group-sm', 'btn-group-xs'] %> <% buttons.forEach(function(modifier){ %>


      <% }); %> ================================================ FILE: docs/spec-helpers/examples/include.html ================================================ Included file from `spec-helpers/examples/include.html`. ================================================ FILE: docs/spec-helpers/examples/markdown-file.md ================================================ Processed **Markdown** file. ================================================ FILE: docs/spec-helpers/examples/mask-one.html ================================================ one
      ================================================ FILE: docs/spec-helpers/examples/mask-two.html ================================================ two
      <%- includeFiles('three.html') %> ================================================ FILE: docs/spec-helpers/examples/three.html ================================================ three
      ================================================ FILE: docs/spec-helpers/info.json ================================================ { "keywords": "ejs, include", "title": "Spec Rendering Helpers", "template": "doc" } ================================================ FILE: docs/spec-helpers/readme.md ================================================ ## Intro Spec pages in SourceJS engine are the main content units. They provide a proper sandbox for demoing rendered components, usage examples and module documentation. Therefore, it's really important to make Spec pages development process as easy as possible. Apart from plugins, that allow integrating different technologies for generating Specs pages, the engine provides a set of native helpers based on [EJS](http://ejs.co). Let's dive deeper into each of available Spec generation features. ## Plugins It's really important for us to make SourceJS easy adaptable to any project needs. Having a powerful, and easy-to-develop plugins system hugely improves developers user experience working with Living Style Guides. Here's a list of all Spec generation focused plugins: * [sourcejs-slm](https://github.com/venticco/sourcejs-slm) - [Slim](http://slim-lang.com/) templates support * [sourcejs-md-react](https://github.com/mik01aj/sourcejs-md-react) - Markdown helpers for rendering React components * [sourcejs-react](https://github.com/szarouski/sourcejs-react) - generate specs from JSX files * [sourcejs-contrib-dss](http://github.com/sourcejs/sourcejs-contrib-dss) - generating documentation out of CSS comments * [sourcejs-jade](https://github.com/sourcejs/sourcejs-jade) - [Jade](http://jade-lang.com/) syntax support Most of the plugins have [live examples](http://sourcejs.com/specs/example-specs-showcase/) on the project website. ## Native Templating SourceJS uses powerful [EJS](http://ejs.co/) templating engine for core UI generation and Specs content processing. This means that any Spec file can use the full power of EJS templating logic. ```html <% if (info.title === 'Title') {% > Action! <% } %> ``` Use plain JavaScript for managing conditions and generating demo content through data loops. All `info.json` file contents are by default available in each Spec scope. Apart from common [meta information](/docs/info-json) available in `info.json` files, it's possible to set any of your own custom data. ```html <h2><%- info.title %></h2> <% var buttons = ['btn-group-lg', '', 'btn-group-sm', 'btn-group-xs'] %> <% buttons.forEach(function(modifier){ %> <div class="btn-group <%= modifier %>" role="group" aria-label="Default button group"> <button type="button" class="btn btn-default">Left</button> <button type="button" class="btn btn-default">Middle</button> <button type="button" class="btn btn-default">Right</button> </div><br><br> <% }); %> ``` ```example <%- include('examples/buttons.html') %> ``` ### Includes Providing a relative path to current Specs, it's easy to include any file. ```html <%- include('examples/include.html') %> ``` ```example <%- include('examples/include.html') %> ``` Note that by default, SourceJS restricts including files outside project directory for security reasons. To disable this restrictions, change `core.sandboxIncludes` configuration. View examples source code. ## EJS Custom Helpers Starting from v.0.5.6, SourceJS provides a set of custom EJS helpers: * `includeMD(path)` - include and process Markdown file * `includeFiles(glob)` - include a set of files by mask (uses [glob](https://github.com/isaacs/node-glob)) Feel free to create Pull Requests with a wider set of helpers. ### includeMD ```html <%- includeMD('examples/markdown-file') %> ``` ```example <%- includeMD('examples/markdown-file') %> ``` ### includeFiles ```html <%- includeFiles('examples/mask-*.html') %> ``` ```example <%- includeFiles('examples/mask-*.html') %> ``` View examples source code. ## Extended features For any other custom Spec content processing, we recommend building own SourceJS plugins. Follow our [instructions](/docs/api/plugins/) and example plugins for fast kick-off. It's also possible to configure custom build task, that will generate compatible Spec pages via Grunt/Gulp or any other build tool. ================================================ FILE: docs/starting/css/starting.css ================================================ /* Starting template ---------------------------------------------------------------------------------- */ /* /Starting template ---------------------------------------------------------------------------------- */ ================================================ FILE: docs/starting/index.src.html ================================================ 

      Starting Spec Page Template

      Developer: Name
      Designer: Name

      Useful information regarding this spec can be left for your team members here.

      Spec Section Heading

      Spec Page Documentation

      Useful notes.

      Rendered code examples.

      This spec uses index.src.html template, view it's source.

      ================================================ FILE: docs/starting/info.json ================================================ { "author": "Spec author", "title": "Starting Spec Page Template", "tag": ["templates"] } ================================================ FILE: docs/starting-md/css/starting.css ================================================ /* Starting template ---------------------------------------------------------------------------------- */ /* /Starting template ---------------------------------------------------------------------------------- */ ================================================ FILE: docs/starting-md/info.json ================================================ { "author": "Spec author", "title": "Markdown Spec Page Template", "tag": ["templates"] } ================================================ FILE: docs/starting-md/readme.md ================================================  # Markdown Spec Page Template Developer: Name
      Designer: Name Useful information regarding this spec can be left for your team members here. ## Spec Section Heading Spec Page Documentation Useful notes. ```example

      Rendered code examples.

      ``` This spec uses readme.md template, view it's source. ================================================ FILE: docs/test-specs/styles/includes/all-tags.html ================================================ Some text some link. Strong, b, em, i, s.

      In text in P tag some link. Some text some link. Strong, b, em, i, s.

      • List 1
      • List 2 short guide
      • List 3 - Long text Lorem ipsum dolor sit amet, consectetur adipiscing elit. At tu eadem ista dic in iudicio aut, si coronam times, dic in senatu. Aliter enim explicari, quod quaeritur, non potest. Quid, quod res alia tota est?
      • List 4
      1. List 1
      2. List 2 short guide
      3. List 3 - Long text Lorem ipsum dolor sit amet, consectetur adipiscing elit. At tu eadem ista dic in iudicio aut, si coronam times, dic in senatu. Aliter enim explicari, quod quaeritur, non potest. Quid, quod res alia tota est?
      4. List 4

      For text description, SourceJS uses basic HTML markup, and custom CSS class hooks. To render highlighted description block like this, add .source_info class to a any HTML element on page.

      name version bundle
      widget-estatement short guide launchpad
      code full

      code in p

      code in div

      ================================================ FILE: docs/test-specs/styles/index.src.html ================================================ 
      <%- include('includes/all-tags.html') %>
      <%- include('includes/all-tags.html') %>
      <%- include('includes/all-tags.html') %>

      Links can be highlighted as link to design specification by adding .source_a_d as a class on the link

      Links can be highlighted as link to other specification by adding .source_a_s as a class on the link

      Links can be highlighted by adding .source_a_hl as a class on the link

      Highlighting can be achieved by either wrapping text in <code> tags, or by using the .source_hl class.

      Spec Section Heading

      Links can be highlighted by adding .source_a_hl as a class on the link

      Notes specific to particular interface elements can be documented here. Heading numbering is generated dynamically, and is supported up to 3 levels deep.

      Hello world;

      Rendered code examples - main documentation blocks.

      Engine decorative styles do not affect this block. Link your project files and start prototyping.

      This page has Twitter Bootstrap styles linked as Master App Styles.

      Hey!
      You can fill custom source code blocks before "source_example". If <code class="src-html"> is not set, it will be created and filled automatically from "source_example" contents.

      Click "Show source" in the inner navigation menu on the right to reveal the source for all examples.

      To render and highlight the source code, wrap it in <code class="src-html">, <code class="src-css">
      or <code class="src-js"> tags, depending on the language you want to highlight, like this:

      <code class="src-html">
      Bar
      </code>

      h3

      some

      h4

      some

      Linking Your Project's CSS

      <link href="/docs/data/bootstrap.css" rel="stylesheet"> <link href="css/spec.css" rel="stylesheet">

      Spec Page Markup

      <%- include('includes/all-tags.html') %>
      <%- include('includes/all-tags.html') %>
      <%- include('includes/all-tags.html') %>
      <%- include('includes/all-tags.html') %>
      <%- include('includes/all-tags.html') %>

      Source code

      This spec uses index.src.html template, view it's source.

      ================================================ FILE: docs/test-specs/styles/info.json ================================================ { "template": "doc", "title": "Spec Styles Test Page (кириллица)", "tag": ["hidden"] } ================================================ FILE: options.js ================================================ // Default SourceJS engine configuration module.exports = { // Restart the app after changing core (back-end) options // Core options could be only redefined from user/options.js, context options are not supported core : { common: { defaultLogLevel: 'INFO', defaultProdLogLevel: 'ERROR', includedDirs: ['docs'], specPaths: ['specs'], // Turn on context level setting contextOptions: true, // Name of context level settings file contextOptionsFile: 'sourcejs-options.js', // Path to your SourceJS configuration folder pathToUser: 'user', // Name of spec meta info file infoFile: 'info.json', // Name of options field in info.json, used to override configuration per spec infoFileOptions: 'sourcejs' }, // Server options are passed to app.listen (https://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback) server: { port: 8080, hostname: undefined }, api: { specsData: 'core/api/data/pages-tree.json', htmlData: 'core/api/data/html.json', specsTestData: 'test/data/api-test-specs.json', htmlTestData: 'test/data/api-test-html.json' }, // Spec catalogs navigation tree fileTree: { // Exclude files from file-tree indexing (first level, in user folder) excludedDirs: ['node_modules', 'bower_components', 'data', 'plugins', '.git', '.idea'], // Exclude files from file-tree indexing (on any level, by folder name) excludedDirsGlobal: ['node_modules', '.git', '.idea'], // Update navigation tree by cron task (setTimeout) cron: false, // Update navigation tree when somebody visits main page mainPageTrigger: false, // Default thumbnail file path (relative to each spec) thumbnail: 'thumbnail.png' }, watch: { enabled: true, foreverWatchEnabled: true }, tracking: { // Anonymous user statistics tracking. // Used to get insights about the community and improve engine usage experience. enabled: true }, // Limits EJS includes, allowing only files in project root sandboxIncludes: true }, // Page rendering configuration (redefinable from context options) rendering: { // Define priority of spec file source specFiles: [ 'index.src', 'index.src.html', 'index.jade', // https://www.npmjs.com/package/sourcejs-jade 'index.jsx', // https://www.npmjs.com/package/sourcejs-react 'index.md', 'readme.md', 'README.md', 'index.html' ], // Define views for rendering SourceJS pages (array order define priority) views: { defaultViewPaths: [ '$(user)/core/views', '$(sourcejs)/core/views' ], spec: [ '$(user)/core/views/spec.ejs', '$(sourcejs)/core/views/spec.ejs' ], navigation: [ '$(user)/core/views/navigation.ejs', '$(sourcejs)/core/views/navigation.ejs' ] } }, // Client-side options (redefinable from context options) assets: { // Page classes containerClass : 'source_container', headerClass : 'source_header', SECTION_CLASS : 'source_section', exampleSectionClass : 'source_example', exampleCleanClass : 'source_clean', mainClass : 'source_main', mainNav : 'source_main_nav', colMain : 'source_col-main', // Core modules modulesEnabled : { // Enable clarify helper links in spec clarifyInSpec: true, htmlAPISync: true, headerFooter: true, specDecorations: true, codeSource: true, sectionFolding: true, innerNavigation: true, // Trims paces in example sections to emulate HTML minify, off by default trimSpaces: false, scrollToHash: true, sections: true, globalNav: true, search: true, loadEvents: true, navHighlight: true, // Enable github auth toolbar links auth: false }, modulesOptions : { navHighlight: { // Page navigation hash update on scroll turned off because of performance issues updateHash: false }, search: { autofocusOnNavigationPage: true, autofocusOnSpecPage: false, activateOnLoad: true } }, // Landing page options for moduleLoader (override without extend) navPageModulesBuild: { modulesEnabled : { headerFooter: true, specDecorations: true, globalNav: true, search: true }, pluginsEnabled: {}, npmPluginsEnabled: {} }, // Legacy options object support for some older plugins (todo:remove with next major release) pluginsOptions: {} }, // External plugins options (are also exposed to client-side plugins: { // PhantomJS HTML API parser will be moved to plugin at v.0.6 htmlParser: { enabled: false } }, /* * Please pay your attention. This is DEMO github applicatio key. * * To get your own one please use github applications service. * Please visit this link to get more information: * https://developer.github.com/guides/basics-of-authentication/#registering-your-app * Current demo key is used in test mode for http://127.0.0.1:8080 */ github: { appId: 'cf00a9e7ee5d9d6af36f', appSecret: 'aebe08e0aa66f6911e4f54df81ce64c9d6e0003b' } }; ================================================ FILE: package.json ================================================ { "name": "sourcejs", "description": "Living Style Guide Engine for Managing and Developing Front-end Components.", "author": "SourceJS", "version": "0.5.6", "license": "MIT", "engines": { "node": ">=0.10.0 <4.0" }, "repository": { "type": "git", "url": "https://github.com/sourcejs/Source.git" }, "dependencies": { "async": "~0.9.0", "body-parser": "~1.6.4", "cheerio": "^0.19.0", "colors": "0.6.x", "commander": "^2.8.1", "compression": "~1.0.11", "cookie-parser": "~1.3.2", "deep-extend": "~0.2.11", "device": "^0.3.2", "ejs": "operatino/ejs#2.3.4-sandbox-option", "everyauth": "^0.4.9", "express": "~4.8.3", "express-session": "^1.10.2", "extend": "~1.2.1", "flat": "^1.2.1", "fs-extra": "~0.11.1", "fs-finder": "operatino/node-fs-finder#fix-windows-find-up", "glob": "^5.0.14", "grunt": "~0.4.5", "grunt-autoprefixer": "~1.0.0", "grunt-cli": "^0.1.13", "grunt-contrib-clean": "^0.6.0", "grunt-contrib-copy": "~0.5.0", "grunt-contrib-cssmin": "~0.6.2", "grunt-contrib-htmlmin": "~0.3.0", "grunt-contrib-jshint": "^0.11.2", "grunt-contrib-less": "~0.11.0", "grunt-contrib-uglify": "~0.2.7", "grunt-contrib-watch": "~0.5.3", "grunt-jsdoc": "^0.5.7", "grunt-newer": "~0.6.1", "gulp-watch": "operatino/gulp-watch#4.3.5-latest-fsevents", "jsdom": "^1.2.1", "load-grunt-parent-tasks": "^0.1.1", "load-grunt-tasks": "~0.3.0", "lodash": "^3.10.1", "log4js": "~0.6.20", "macaddress": "^0.2.8", "marked": "^0.3.2", "path": "^0.4.9", "phantomjs": "1.9.7-15", "pretty-hrtime": "^1.0.0", "q": "^1.1.1", "serve-favicon": "^2.2.0", "shortid": "^2.2.2", "time-grunt": "~0.2.10", "tinyforever": "0.0.3", "universal-analytics": "^0.3.9" }, "scripts": { "postinstall": "node ./core/postInstall && grunt update && node ./core/trackInstall", "build": "npm i", "lint": "grunt jshint", "start": "node app", "watch": "grunt watch-all", "watch:css": "grunt watch-css", "test": "grunt ci-pre-run && node app.js --test", "test:unit": "grunt test", "test:func": "grunt test-func", "ci-test": "grunt ci-pre-run && node app.js --test --log trace", "ci-test-win": "grunt ci-pre-run && node app.js --post-grunt ci-post-run-win --test --log trace --no-watch" }, "devDependencies": { "assert": "~1.1.1", "chai": "^3.2.0", "cross-spawn": "^0.4.0", "grunt-casperjs": "^2.1.0", "grunt-mocha-test": "^0.12.4", "mocha": "^2.0.1", "should": "~4.0.4", "supertest": "~0.13.0" } } ================================================ FILE: source.sh ================================================ #!/bin/bash # # description: SourceJS service, for managing production server, using node-forever. # processname: node # # Based on https://gist.github.com/jinze/3748766 # # To use it as service on Ubuntu: # sudo cp source.sh /etc/init.d/source # sudo chmod a+x /etc/init.d/source # sudo update-rc.d source defaults # # Then use commands: # sudo service source NAME=SourceJS # Unique name for the application USER=user # User for process running HOME_DIR=/home/user # User home dir SOUREC_DIR=$HOME_DIR/Source # Location of the application source COMMAND=node # Command to run APP_PATH=$SOUREC_DIR/app.js # Application entry point script NODE_ENVIRONMENT=production # Node environment FOREVER=forever start() { echo "Starting $NAME node instance : " sudo -H -u $USER NODE_ENV=$NODE_ENVIRONMENT $FOREVER start -a -c $COMMAND $APP_PATH RETVAL=$? } restart() { echo "Restarting $NAME node instance : " sudo -H -u $USER $FOREVER restart $APP_PATH RETVAL=$? } status() { echo "Status for $NAME : " sudo -H -u $USER $FOREVER list RETVAL=$? } stop() { echo "Shutting down $NAME node instance." sudo -H -u $USER $FOREVER stop $APP_PATH RETVAL=$? } case "$1" in start) start ;; stop) stop ;; status) status ;; restart) restart ;; *) echo "Usage: {start|stop|status|restart}" exit 1 ;; esac exit $RETVAL ================================================ FILE: test/data/api-test-html.json ================================================ { "base-test": { "button": { "specFile": { "id": "base-test/button", "css": "", "js": "", "contents": [ { "id": "1", "class": "", "title": "", "html": [ "" ], "nested": [ { "id": "1.1", "class": "", "title": "", "html": [ "" ], "nested": [] } ] }, { "id": "2", "class": "", "title": "", "html": [ "" ], "nested": [] } ] } }, "nav-sm": { "specFile": { "id": "base-test/nav-sm", "css": "", "js": "", "contents": [ { "id": "1", "class": "", "title": "", "html": [ "" ] }, { "id": "2", "class": "", "title": "", "html": [ "" ] } ] } } } } ================================================ FILE: test/data/api-test-specs.json ================================================ { "base-test": { "base": { "specFile": { "url": "/base/base", "lastmod": "12.8.2014", "lastmodSec": 1407856231000, "fileName": "index.html", "thumbnail": false, "author": "Evgeny Khoroshilov", "title": "Мелкие стандарты", "tag": [ "html", "tag" ], "info": "Страница с описанием небольших стартов, которые не доросли до своей спеки." } }, "button": { "specFile": { "url": "/base/button", "lastmod": "12.8.2014", "lastmodSec": 1407856231000, "fileName": "index.html", "thumbnail": true, "tag": [ "some" ], "keywords": "base, standard", "author": "Evgeny Khoroshilov", "title": "Мелкие стандарты" } } }, "project-test": { "project-spec": { "specFile": { "url": "/project/project-spec", "lastmod": "12.8.2014", "lastmodSec": 1407856231000, "fileName": "index.html" } } }, "specFile": { "url": "", "lastmod": "31.7.2014", "lastmodSec": 1406793767000, "fileName": "index.src", "thumbnail": false, "title": "OK Prototype", "role": "navigation" } } ================================================ FILE: test/functional/common.js ================================================ var appPort = casper.cli.get("app-port") || 8080; var url = 'http://127.0.0.1:' + appPort; casper.options.viewportSize = {width: 1024, height: 768}; var urlsToCheck = [ url, url + '/docs/spec/', url + '/docs/' ]; var error = {}; casper.on("page.error", function(msg, trace) { error.msg = msg; this.echo("Error: " + msg, "ERROR"); this.echo("file: " + trace[0].file, "WARNING"); this.echo("line: " + trace[0].line, "WARNING"); this.echo("function: " + trace[0]["function"], "WARNING"); }); urlsToCheck.forEach(function(item){ casper.test.begin('Check availability and JS errors on ' + item, 2, function(test) { error = {}; casper.start(item).then(function(response) { casper.wait(500, function(){ // Slow down a bit, because of strange API bug }); if (response.status !== 200) { test.fail("Page load error, expected status 200, got " + response.status); } else { test.pass("Status 200 OK"); } }).then(function() { if (typeof error.msg === 'string') { test.fail("JS errors found: "+ error.msg); } else { test.pass("No JS errors"); } }).run(function() { test.done() }).clear(); }); }); ================================================ FILE: test/functional/globalNav.js ================================================ var appPort = casper.cli.get('app-port') || 8080; var url = 'http://127.0.0.1:' + appPort + '/docs/'; casper.options.viewportSize = {width: 1024, height: 768}; casper.test.begin('Check navigaton page', 3, function suite(test) { casper.start(url).then(function() { var _this = this; var nav = '.source_catalog_list .source_catalog_list_i'; this.waitForSelector(nav, function pass() { test.assertEval(function (nav) { return document.querySelectorAll(nav).length > 5; }, 'Should have more than 5 nav items', [nav]); test.assertEval(function (nav) { return !!document.querySelector(nav + ' .source_catalog_a[href="/docs/base"]') && !!document.querySelector(nav + ' .source_catalog_a[href="/docs/clarify"]') && !!document.querySelector(nav + ' .source_catalog_a[href="/docs/starting"]'); }, 'Right nav items in set', [nav]); }, function fail() { test.fail(nav); } ); }).then(function(){ this.click('.source_catalog_image-tumbler'); test.assertExists('.source_catalog.__show-preview', 'Show preview toggled'); }).run(function() { test.done() }).clear(); }); ================================================ FILE: test/functional/search.js ================================================ var appPort = casper.cli.get('app-port') || 8080; var url = 'http://127.0.0.1:' + appPort; casper.options.viewportSize = {width: 1024, height: 768}; casper.test.begin('Checking search', 3, function suite(test) { casper.start(url).then(function() { var _this = this; var input = '.source_search .source_search_it'; var autoComplete = '.autocomplete-wrapper .autocomplete-suggestion:first-child a'; var searchField = '.source_search_it[data-initialized]'; test.assertExists(input, 'Search input exists'); this.waitForSelector(searchField, function pass() { casper.sendKeys(searchField, 'main', {keepFocus: true}); _this.waitForSelector(autoComplete, function pass() { this.click(autoComplete); }, function fail() { test.fail(autoComplete); } ); }, function fail() { test.fail(searchField); } ); }).then(function() { this.waitFor( function check() { return (this.getCurrentUrl() === url+'/docs/base/'); }, function then() { // step to execute when check() is ok test.assertExists('.source_main > h1', 'Spec header exists'); test.assertEquals(this.getCurrentUrl(), url+'/docs/base/', 'New page URL is right') }, function timeout() { // step to execute if check has failed this.echo('Failed to navigate to search result'); } ); }).run(function() { test.done() }).clear(); }); ================================================ FILE: test/functional/snippets.md ================================================ # CasperJS code snippets Timeout ``` casper.wait(1000, function () { }); ``` Screenshot ``` this.capture('test.png'); this.capture('test.png', { top: 0, left: 0, width: 1024, height: 768 }); ``` Wait for ``` _this.waitForSelector(autoComplete, function pass() { this.click(autoComplete); }, function fail() { test.fail(autoComplete); } ); this.waitFor( function check() { return (this.getCurrentUrl() === url+'/docs/base/'); }, function then() { // step to execute when check() is ok test.assertExists('.source_main > h1', 'Spec header exists'); test.assertEquals(this.getCurrentUrl(), url+'/docs/base/', 'New page URL is right') }, function timeout() { // step to execute if check has failed this.echo('Failed to navigate to search result'); } ); ``` Eval ``` var nav = 'some' test.assertEval(function (nav) { return document.querySelectorAll(nav).length > 5; }, 'Should have more than 5 nav items', [nav]); var js = this.evaluate(function() { return document; }); this.echo(js.all[0].outerHTML); ``` ================================================ FILE: test/functional/specpage.js ================================================ var appPort = casper.cli.get('app-port') || 8080; var url = 'http://127.0.0.1:' + appPort + '/docs/spec/'; casper.options.viewportSize = {width: 1024, height: 768}; casper.test.begin('Checking inner navigation', 3, function suite(test) { casper.start(url).then(function() { var _this = this; var menu = '.source_nav.__loaded'; this.waitForSelector(menu, function pass() { this.click('.source_nav .source_main_nav_li:nth-child(2) a'); test.assertEquals(this.getCurrentUrl(), url+'#2', 'URL with hash is right') }, function fail() { test.fail(menu); } ); }).then(function() { var highlighted = '.source_nav .source_main_nav_li:nth-child(2).__active a.__active'; this.waitForSelector(highlighted, function pass() { test.assertEval(function () { return window.pageYOffset > 1000 && window.pageYOffset < 2500; }, 'Page should be scrolled to section'); test.assertExists(highlighted, 'Menu item highlighted'); }, function fail() { test.fail(highlighted); } ); }).run(function() { test.done() }).clear(); }); casper.test.begin('Code source', 2, function suite(test) { casper.start(url).then(function() { var _this = this; var actionItem = '.source_main_nav_ac_item.source_source-code_action-item'; this.waitForSelector(actionItem, function pass() { this.click(actionItem + ' .source_slider_frame'); test.assertExists(actionItem + ' .source_slider_frame.source_slider_frame__on', 'Toggler is highlighted'); }, function fail() { test.fail(actionItem); } ); }).then(function() { var codeSource = '.source_source-code.source_source-code__show'; this.waitForSelector(codeSource, function pass() { test.assertExists(codeSource, 'Code source is shown'); }, function fail() { test.fail(codeSource); } ); }).run(function() { test.done() }).clear(); }); casper.test.begin('Hash link openning', 2, function suite(test) { casper.start(url + '#2').then(function() { var highlighted = '.source_nav .source_main_nav_li:nth-child(2).__active a.__active'; this.waitForSelector(highlighted, function pass() { test.assertEval(function () { return window.pageYOffset > 1000 && window.pageYOffset < 2500; }, 'Page should be scrolled to section'); test.assertExists(highlighted, 'Menu item highlighted'); }, function fail() { test.fail(highlighted); } ); }).run(function() { test.done() }).clear(); }); ================================================ FILE: test/unit/api/html.js ================================================ var should = require('should'); var request = require('supertest'); describe('API test /api/specs/html', function () { var url = 'http://127.0.0.1:8080'; describe('GET /api/specs/html', function () { it('should return list of html', function (done) { var body = { }; request(url) .get('/api-test/specs/html') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/button'); done(); }); }); it('should return HTML by ID', function (done) { var body = { id: 'base-test/button' }; request(url) .get('/api-test/specs/html') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('contents'); done(); }); }); it('should return HTML by ID and by section', function (done) { var body = { id: 'base-test/button', sections: '1' }; request(url) .get('/api-test/specs/html') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; var section1 = res.body.contents.length === 1; section1.should.be.ok; done(); }); }); it('should NOT return HTML of non existent section', function (done) { var body = { id: 'base-test/button', sections: '11' }; request(url) .get('/api-test/specs/html') .expect(404) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; var section = !!(res.body.contents && res.body.contents.length === 1); section.should.not.be.ok; done(); }); }); }); describe('POST /api/specs/html', function () { it('it should POST new spec and return updated object', function (done) { var body = { data: { "base-test": { "posted": { "specFile": { "description": "This spec was posted through API", "id": "base-test/posted", "contents": [ { "id": "1", "class": "", "title": "", "html": [ "" ], "nested": [] } ] } } } } }; request(url) .post('/api-test/specs/html') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body['base-test'].should.have.property('posted'); done(); }); }); }); describe('DELETE /api/specs/html', function () { it('it should return object without "base-test/posted"', function (done) { var body = { id: 'base-test/posted' }; request(url) .delete('/api-test/specs/html') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.body['base-test'].should.not.have.property('posted'); done(); }); }); }); }); ================================================ FILE: test/unit/api/specs.js ================================================ var should = require('should'); var assert = require('assert'); var request = require('supertest'); var path = require('path'); var pathToMasterApp = path.resolve('./'); global.pathToApp = pathToMasterApp; var parseData = require(path.join(pathToMasterApp, 'core/lib/parseData')); var loadOptions = require(path.join(pathToMasterApp, 'core/loadOptions')); global.opts = loadOptions(path.resolve(pathToMasterApp)); var parseSpecs = new parseData({ scope: 'specs', path: global.opts.core.api.specsData }); describe('Internal API tests', function () { describe('Check parseData:specs', function () { it('should return list of specs', function (done) { var data = parseSpecs.getAll(); data.should.have.property('docs/base'); done(); }); it('should return spec by ID', function (done) { var data = parseSpecs.getByID('docs/base'); data.should.have.property('url'); done(); }); }); }); describe('API test /api/specs', function () { var url = 'http://127.0.0.1:8080'; describe('Check real API data available', function () { it('should return list of specs', function (done) { var body = { }; request(url) .get('/api/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('docs/base'); done(); }); }); }); describe('GET /api/specs', function () { it('should return list of specs', function (done) { var body = { }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); done(); }); }); it('should return spec by ID', function (done) { var body = { id: 'base-test/base' }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('url'); res.body.should.have.property('lastmod'); done(); }); }); it('should not have cat info', function (done) { var body = { }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.not.have.property('specFile'); done(); }); }); }); describe('GET /api/specs with fields filter', function () { it('should return only specs WITH info field', function (done) { var body = { filter: { fields:["info"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); it('should return only specs WITH keywords, info, and title field', function (done) { var body = { filter: { fields:["keywords","info","title"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.not.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); it('should return only specs WITHOUT info', function (done) { var body = { filterOut: { fields:["info"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/button'); res.body.should.not.have.property('base-test/base'); done(); }); }); it('should return only specs WITHOUT url, info and cat', function (done) { var body = { filterOut: { fields:["url","info","cat"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.not.have.property('base-test/button'); res.body.should.not.have.property('base-test/base'); done(); }); }); it('should return only specs WITH info and WITHOUT keywords', function (done) { var body = { filter: { fields:["info"] }, filterOut: { fields:["keywords"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); it('should return only specs WITH info and cat but WITHOUT keywords and thumbnail', function (done) { var body = { filter: { fields:["info","tag"] }, filterOut: { fields:["keywords"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); }); describe('GET /api/specs with tag filter', function () { it('should return only specs WITH "html" tag', function (done) { var body = { filter: { tags:["html"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); it('should return only specs WITH "html" and "tag" tags', function (done) { var body = { filter: { tags:["html","tag"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); it('should return only specs WITHOUT "html" and "tag" tags', function (done) { var body = { filterOut: { tags:["html","tag"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.not.have.property('base-test/base'); res.body.should.have.property('base-test/button'); done(); }); }); it('should return only specs WITH "html" and WITHOUT "some" tags', function (done) { var body = { filter: { tags:["html"] }, filterOut: { tags:["some"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('base-test/base'); res.body.should.not.have.property('base-test/button'); done(); }); }); }); describe('GET /api/specs with cats filter', function () { it('should return only project cat specs', function (done) { var body = { filter: { cats:["project-test"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.have.property('project-test/project-spec'); res.body.should.not.have.property('base-test/button'); done(); }); }); it('should return only base cat specs', function (done) { var body = { filter: { cats:["base"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.not.have.property('project-test/project-spec'); res.body.should.have.property('base-test/button'); done(); }); }); it('should return all except project specs', function (done) { var body = { filterOut: { cats:["project-test"] } }; request(url) .get('/api-test/specs') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } res.should.be.json; res.body.should.not.have.property('project-test/project-spec'); res.body.should.have.property('base-test/button'); done(); }); }); }); }); ================================================ FILE: test/unit/ejsHelpersSpec.js ================================================ var path = require('path'); var expect = require('chai').expect; var pathToMasterApp = path.resolve('./'); var ejs = require(path.join(pathToMasterApp, 'core/ejsWithHelpers.js')); var loadOptions = require(path.join(pathToMasterApp, 'core/loadOptions')); global.opts = loadOptions(path.resolve(pathToMasterApp)); describe('includeMD', function () { it('should properly include Markdown file', function (done) { var result = ejs.render('<%- includeMD("partials/markdown") %>', { filename: __filename }); expect(result).to.equal('

      Hello world!

      \n'); done(); }); it('should properly include Markdown file with EJS includes', function (done) { var result = ejs.render('<%- includeMD("partials/markdown-ejs") %>', { world: 'world' }, { filename: __filename }); expect(result).to.equal('

      Hello world!

      \n'); done(); }); it('should properly include Markdown with nested include', function (done) { var result = ejs.render('<%- includeMD("partials/markdown-ejs-nested") %>', { world: 'world' }, { filename: __filename }); expect(result).to.contain('

      Nested:

      Hello world!

      '); done(); }); }); describe('includeFiles', function () { it('should include one file by glob', function (done) { var result = ejs.render('<%- includeFiles("partials/mask-one.html") %>', { filename: __filename }); expect(result).to.equal('one
      '); done(); }); it('should include multiple files by glob', function (done) { var result = ejs.render('<%- includeFiles("partials/mask-*.html") %>', { filename: __filename }); expect(result).to.contain('one'); expect(result).to.contain('two'); expect(result).to.contain('three'); expect(result).to.contain('Hello'); done(); }); }); ================================================ FILE: test/unit/lib/extendTillSpec.js ================================================ var should = require('should'); var assert = require('assert'); var path = require('path'); var pathToMasterApp = path.resolve('./'); var extendTillSpec = require(path.join(pathToMasterApp, 'core/lib/extendTillSpec')); var target = { base: { btn: { specFile: { some: [ 'a', 'b', 'c' ] } }, test: 'bgg' }, project: 'some' }; var src = { base: { btn: { specFile: { some: [ 'a' ] } } } }; target = extendTillSpec(target, src); describe('extendTillSpec module check', function () { it('should overwrite all in specFile', function (done) { var length = target.base.btn.specFile.some.length; length.should.equal(1); done(); }); it('should extend everything till specFile', function (done) { target.project.should.be.type('string'); target.base.test.should.be.type('string'); done(); }); }); ================================================ FILE: test/unit/lib/utils.js ================================================ var should = require('should'); var assert = require('assert'); var path = require('path'); var pathToMasterApp = path.resolve('./'); var utils = require(path.join(pathToMasterApp, 'core/lib/utils')); describe('utils: extendOptions', function () { it('it should extend objects skipping arrays merge', function (done) { var input = { a: { foo: "bar" }, b: ['foo', 'bar'] }; utils.extendOptions(input, { a: { bar: "foo" }, b: ['bar'], c: 'val' }); input.should.have.property('c', 'val'); input.a.should.have.property('foo', 'bar'); input.a.should.have.property('bar', 'foo'); input.should.have.property('b').with.lengthOf(1); input.should.have.property('b', ['bar']); done(); }); it('it should accept multiple objects', function (done) { var input = {}; utils.extendOptions(input, {a: 'val', arr: ['foo', 'bar', 'foo']}, {'b': 'val'}, {c: 'val', arr: ['bar', 'foo']}); input.should.have.property('a', 'val'); input.should.have.property('b', 'val'); input.should.have.property('c', 'val'); input.should.have.property('arr', ['bar', 'foo']); done(); }); }); ================================================ FILE: test/unit/middleware/clarify.js ================================================ var fs = require('fs'); var path = require('path'); var pathToMasterApp = path.resolve('./'); var jq = fs.readFileSync(path.join(pathToMasterApp,'assets/js/lib/jquery-2.1.4.min.js'), "utf-8"); var should = require('should'); var assert = require('assert'); var request = require('supertest'); var jsdom = require('jsdom'); var loadOptions = require(path.join(pathToMasterApp, 'core/loadOptions')); global.opts = loadOptions(path.resolve(pathToMasterApp)); describe('Clarify test /docs/spec?clarify=true', function () { describe('GET from JSDOM /docs/spec?clarify=true...', function () { var url = 'http://localhost:8080/docs/spec/?clarify=true'; it('should return nothing (§ions=77)', function (done) { request(url) .get('§ions=77') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var examples = $('.source_example'); examples.length.should.equal(0); done(); } }); }); }); it('should return all sections', function (done) { request(url) .get() .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var sectionHeaders = $('.source_section_h'); var examples = $('.source_example'); sectionHeaders.length.should.be.greaterThan(1); examples.length.should.be.greaterThan(0); done(); } }); }); }); it('should return one example', function (done) { request(url) .get('§ions=1.1') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var examples = $('.source_example'); examples.length.should.equal(1); done(); } }); }); }); it('should have injected resources', function (done) { request(url) .get('§ions=1.1') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; $('[href*="bootstrap.css"]').length.should.equal(1); $('body > style').length.should.be.greaterThan(0); $('body > script').length.should.be.greaterThan(0); done(); } }); }); }); it('should change template', function (done) { request(url) .get('&tpl=clear') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var examples = $('.source_example'); $('.__clear').length.should.equal(1); examples.length.should.equal(0); done(); } }); }); }); }); describe('GET from API /docs/spec?clarify=true&fromApi=true...', function () { var urlFromApi = 'http://localhost:8080/docs/spec/?clarify=true&fromApi=true'; it('should return nothing (§ions=77&apiUpdate=true)', function (done) { this.timeout(10000); request(urlFromApi) .get('§ions=77&apiUpdate=true') .expect(500) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var examples = $('.source_example'); examples.length.should.equal(0); done(); } }); }); }); if (global.opts.plugins && global.opts.plugins.htmlParser && global.opts.plugins.htmlParser.enabled) { it('should return all sections', function (done) { request(urlFromApi) .get() .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var sectionHeaders = $('.source_section_h'); var examples = $('.source_example'); sectionHeaders.length.should.be.greaterThan(1); examples.length.should.be.greaterThan(0); done(); } }); }); }); it('should return one example', function (done) { request(urlFromApi) .get('§ions=1.1') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var examples = $('.source_example'); examples.length.should.equal(1); done(); } }); }); }); it('should have injected resources', function (done) { request(urlFromApi) .get('§ions=1.1') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; $('[href*="bootstrap.css"]').length.should.equal(1); $('body > style').length.should.be.greaterThan(0); $('body > script').length.should.be.greaterThan(0); done(); } }); }); }); it('should change template', function (done) { request(urlFromApi) .get('&tpl=clear') .expect(200) .end(function (err, res) { if (err) { throw err; } jsdom.env({ html: res.text, src: [jq], done: function (errors, window) { var $ = window.$; var examples = $('.source_example'); $('.__clear').length.should.equal(1); examples.length.should.equal(0); done(); } }); }); }); } }); }); ================================================ FILE: test/unit/middleware/md.js ================================================ var should = require('should'); var request = require('supertest'); var cheerio = require('cheerio'); describe('Test .md support', function () { var url = 'http://127.0.0.1:8080'; describe('GET /docs/markdown/', function () { it('should render h1', function (done) { var body = { }; request(url) .get('/docs/markdown/') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } var $ = cheerio.load('
      '+ res.text +'
      '); var hasDom = $('h1').length === 1; hasDom.should.be.ok; done(); }); }); }); describe('GET /docs/markdown/', function () { it('should render sections', function (done) { var body = { }; request(url) .get('/docs/markdown/') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } var $ = cheerio.load('
      '+ res.text +'
      '); var hasDom = $('.source_section').length >= 1; var oneHeaderPerSection = true; $('.source_section').each(function(){ if ($(this).children('h2').length !== 1) { oneHeaderPerSection = false; } }); hasDom.should.be.ok; oneHeaderPerSection.should.be.ok; done(); }); }); }); describe('GET /docs/markdown/', function () { it('should render code', function (done) { var body = { }; request(url) .get('/docs/markdown/') .expect(200) .send(body) .end(function (err, res) { if (err) { throw err; } var $ = cheerio.load('
      '+ res.text +'
      '); var hasDom = $('code.src-html').length >= 1; hasDom.should.be.ok; done(); }); }); }); }); ================================================ FILE: test/unit/partials/markdown-ejs-nested.md ================================================ Hello **<%= world %>**! Nested:<%- includeMD('markdown.md') %> ================================================ FILE: test/unit/partials/markdown-ejs.md ================================================ Hello **<%= world %>**! ================================================ FILE: test/unit/partials/markdown.md ================================================ Hello **world**! ================================================ FILE: test/unit/partials/mask-one.html ================================================ one
      ================================================ FILE: test/unit/partials/mask-two.html ================================================ two
      <%- includeFiles('three.html') %> ================================================ FILE: test/unit/partials/three.html ================================================ three
      <%- includeMD('markdown.md') %>