Repository: coffeedoc/codo
Branch: master
Commit: a02771cbf23a
Files: 165
Total size: 594.0 KB
Directory structure:
gitextract_nbr74owg/
├── .codoopts
├── .gitignore
├── .travis.yml
├── Gruntfile.coffee
├── LICENSE.md
├── README.md
├── bin/
│ └── codo
├── lib/
│ ├── _entities.coffee
│ ├── _meta.coffee
│ ├── _tools.coffee
│ ├── codo.coffee
│ ├── command.coffee
│ ├── documentation.coffee
│ ├── entities/
│ │ ├── class.coffee
│ │ ├── extra.coffee
│ │ ├── file.coffee
│ │ ├── method.coffee
│ │ ├── mixin.coffee
│ │ ├── property.coffee
│ │ └── variable.coffee
│ ├── entity.coffee
│ ├── environment.coffee
│ ├── meta/
│ │ ├── method.coffee
│ │ └── parameter.coffee
│ ├── tools/
│ │ ├── markdown.coffee
│ │ └── referencer.coffee
│ └── traverser.coffee
├── package.json
├── spec/
│ ├── _templates/
│ │ ├── angular/
│ │ │ ├── angular-with-new.coffee
│ │ │ ├── angular-with-new.json
│ │ │ ├── angular.coffee
│ │ │ └── angular.json
│ │ ├── classes/
│ │ │ ├── class_description_markdown.coffee
│ │ │ ├── class_description_markdown.json
│ │ │ ├── class_documentation.coffee
│ │ │ ├── class_documentation.json
│ │ │ ├── class_extends.coffee
│ │ │ ├── class_extends.json
│ │ │ ├── empty_class.coffee
│ │ │ ├── empty_class.json
│ │ │ ├── export_class.coffee
│ │ │ ├── export_class.json
│ │ │ ├── global_class.coffee
│ │ │ ├── global_class.json
│ │ │ ├── inner_class.coffee
│ │ │ ├── inner_class.json
│ │ │ ├── namespaced_class.coffee
│ │ │ ├── namespaced_class.json
│ │ │ ├── simple_class.coffee
│ │ │ └── simple_class.json
│ │ ├── complicateds/
│ │ │ ├── methods.coffee
│ │ │ └── variables.coffee
│ │ ├── environment/
│ │ │ ├── class.coffee
│ │ │ ├── mixin.coffee
│ │ │ └── result.json
│ │ ├── example/
│ │ │ ├── CHANGELOG
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ └── src/
│ │ │ ├── angry_animal.coffee
│ │ │ ├── animal.coffee
│ │ │ ├── lion.coffee
│ │ │ ├── over_documented_class.coffee
│ │ │ └── over_documented_mixin.coffee
│ │ ├── extras/
│ │ │ ├── README
│ │ │ └── README.md
│ │ ├── files/
│ │ │ ├── non_class_file.coffee
│ │ │ └── non_class_file.json
│ │ ├── methods/
│ │ │ ├── assigned_parameters.coffee
│ │ │ ├── assigned_parameters.json
│ │ │ ├── class_methods.coffee
│ │ │ ├── class_methods.json
│ │ │ ├── curly_method_documentation.coffee
│ │ │ ├── curly_method_documentation.json
│ │ │ ├── dynamic_methods.coffee
│ │ │ ├── dynamic_methods.json
│ │ │ ├── instance_methods.coffee
│ │ │ ├── instance_methods.json
│ │ │ ├── method_documentation.coffee
│ │ │ ├── method_documentation.json
│ │ │ ├── method_events.coffee
│ │ │ ├── method_events.json
│ │ │ ├── method_example.coffee
│ │ │ ├── method_example.json
│ │ │ ├── named_parameters.coffee
│ │ │ ├── named_parameters.json
│ │ │ ├── overload_method.coffee
│ │ │ └── overload_method.json
│ │ ├── mixins/
│ │ │ ├── concern.coffee
│ │ │ ├── concern.json
│ │ │ ├── extend_mixin.coffee
│ │ │ ├── extend_mixin.json
│ │ │ ├── include_mixin.coffee
│ │ │ ├── include_mixin.json
│ │ │ ├── missing_mixins.coffee
│ │ │ ├── missing_mixins.json
│ │ │ ├── mixin_documentation.coffee
│ │ │ ├── mixin_documentation.json
│ │ │ ├── mixin_methods.coffee
│ │ │ └── mixin_methods.json
│ │ ├── properties/
│ │ │ ├── properties.coffee
│ │ │ └── properties.json
│ │ └── variables/
│ │ ├── class_variables.coffee
│ │ ├── class_variables.json
│ │ ├── constant_variables.coffee
│ │ ├── constant_variables.json
│ │ ├── instance_variables.coffee
│ │ └── instance_variables.json
│ ├── lib/
│ │ ├── api_spec.coffee
│ │ ├── codo_spec.coffee
│ │ ├── entities/
│ │ │ ├── class_spec.coffee
│ │ │ └── mixin_spec.coffee
│ │ ├── environment_spec.coffee
│ │ ├── meta/
│ │ │ ├── method_spec.coffee
│ │ │ └── parameter_spec.coffee
│ │ └── tools/
│ │ └── markdown_spec.coffee
│ └── themes/
│ └── default/
│ └── lib/
│ ├── templater_spec.coffee
│ ├── theme_spec.coffee
│ └── tree_builder_spec.coffee
└── themes/
└── default/
├── assets/
│ ├── javascript/
│ │ ├── application.js
│ │ ├── codo.coffee
│ │ ├── frames.coffee
│ │ ├── fuzzy.coffee
│ │ ├── keys.coffee
│ │ ├── sidebar.coffee
│ │ ├── toc.coffee
│ │ └── vendor/
│ │ ├── fuzzy.coffee
│ │ ├── highlight.coffeescript.js
│ │ ├── highlight.js
│ │ ├── jquery.js
│ │ ├── keymaster.js
│ │ └── underscore.js
│ └── stylesheets/
│ ├── alphabetical_index.styl
│ ├── application.styl
│ ├── base.styl
│ ├── class.styl
│ ├── footer.styl
│ ├── header.styl
│ ├── lists.styl
│ ├── toc.styl
│ └── vendor/
│ └── highlight.css
├── lib/
│ ├── _theme.coffee
│ ├── templater.coffee
│ ├── theme.coffee
│ └── tree_builder.coffee
└── templates/
├── alphabetical_index.hamlc
├── class.hamlc
├── class_list.hamlc
├── extra.hamlc
├── extra_list.hamlc
├── file.hamlc
├── file_list.hamlc
├── frames.hamlc
├── layout/
│ ├── footer.hamlc
│ ├── header.hamlc
│ └── intro.hamlc
├── method_list.hamlc
├── mixin.hamlc
├── mixin_list.hamlc
└── partials/
├── documentation.hamlc
├── list_nav.hamlc
├── method_list.hamlc
├── method_signature.hamlc
├── method_summary.hamlc
├── type_link.hamlc
└── variable_list.hamlc
================================================
FILE CONTENTS
================================================
================================================
FILE: .codoopts
================================================
--title "Codo Documentation"
lib/
themes/
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
.bundle
.idea
doc
npm-debug.log
================================================
FILE: .travis.yml
================================================
language: node_js
branches:
only:
- master
- 1.x
notifications:
recipients:
- michi@netzpiraten.ch
- boris@staal.io
================================================
FILE: Gruntfile.coffee
================================================
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-release'
grunt.initConfig
release:
options:
bump: false
add: false
commit: false
push: false
================================================
FILE: LICENSE.md
================================================
# MIT License
Copyright (c) 2012-2013 Michael Kessler, Boris Staal
Template components are derivative works of YARD (http://yardoc.org)
Copyright (c) Loren Segal and licensed under the MIT license
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: README.md
================================================
# Codo [](https://travis-ci.org/coffeedoc/codo)
Codo is a [CoffeeScript](http://coffeescript.org/) API documentation generator, similar to [YARD](http://yardoc.org/).
Its generated documentation is focused on CoffeeScript class syntax for classical inheritance.
## Features
* Detects classes, methods, constants, mixins & concerns.
* Many tags to add semantics to your code.
* Generates a nice site to browse your code documentation in various ways.
* Can be used to assure minimum documentation
* ~~Documentation generation and hosting as a service on [CoffeeDoc.info](http://coffeedoc.info)~~ (we no longer have the access to domain and the service is currently inaccessible)
## Codo in action
Annotate your source with Codo tags to add semantic information to your code. It looks like this:
```CoffeeScript
# Base class for all animals.
#
# @example How to subclass an animal
# class Lion extends Animal
# move: (direction, speed): ->
#
class Example.Animal
# The Answer to the Ultimate Question of Life, the Universe, and Everything
@ANSWER = 42
# Construct a new animal.
#
# @param [String] name the name of the animal
# @param [Date] birthDate when the animal was born
#
constructor: (@name, @birthDate = new Date()) ->
# Move the animal.
#
# @example Move an animal
# new Lion('Simba').move(direction: 'south', speed: 12)
#
# @param [Object] options the moving options
# @option options [String] direction the moving direction
# @option options [Number] speed the speed in mph
#
move: (options = {}) ->
```
Then generate the documentation with the `codo` command line tool. You can browse some
generated Codo documentation on [CoffeeDoc.info](http://coffeedoc.info) to get a feeling how you can navigate in various ways through your code layers.
In the `Example` namespace you'll find some classes and mixins that makes absolutely no sense, its purpose is only to show the many features Codo offers.
## Installation
Codo is available in NPM and can be installed with:
```bash
$ npm install -g codo
```
Please have a look at the [CHANGELOG](https://github.com/coffeedoc/codo/releases) when upgrading to a newer Codo version with `npm update`.
## Tags
You have to annotate your code with Codo tags to give it some meaning to the parser that generates the documentation. Each tag starts with the `@` sign followed by the tag name. See the following overview for a minimal description of all available tags. Most tags are self-explaining and the one that aren't are described afterwards in more detail.
Tags can take multiple lines, just indent subsequent lines by two spaces.
### Overview
The following table shows the list of all available tags in alphabetical order with its expected options. An option in parenthesis is optional and the square brackets are part of the Codo tag format and must actually be written. Some tags can be defined multiple times and they can be applied to different contexts, either in the comment for a class, a comment for a mixin or in a method comment.
Tag format
Multiple occurrences
Classes
Mixins
Methods
@namespace namespace
✔
✔
@abstract (message)
✔
✔
✔
@author name
✔
✔
✔
✔
@concern mixin
✔
✔
@copyright name
✔
✔
✔
@deprecated
✔
✔
✔
@example (title) Code
✔
✔
✔
✔
@extend mixin
✔
✔
@include mixin
✔
✔
@note message
✔
✔
✔
✔
@method signature Method tags
✔
✔
@mixin
✔
@option option [type] name description
✔
✔
@event name [description] Event tags
✔
✔
✔
@overload signature Method tags
✔
✔
@param [type] name description @param name [type] description
✔
✔
@private
✔
✔
✔
@property [type] description
✔
@public
✔
✔
@return [type] description
✔
@see link/reference
✔
✔
✔
✔
@since version
✔
✔
✔
@throw message
✔
✔
@todo message
✔
✔
✔
✔
@version version
✔
✔
✔
@nodoc
✔
✔
✔
### Alternative syntax
You can also use curly braces instead of square brackets if you prefer:
```CoffeeScript
# Move the animal.
#
# @example Move an animal
# new Lion('Simba').move('south', 12)
#
# @param {Object} options the moving options
# @option options {String} direction the moving direction
# @option options {Number} speed the speed in mph
#
move: (options = {}) ->
```
It's also possible to use [CoffeeScript block comments](http://coffeescript.org/#strings) instead of the normal
comments. If you solely use block comments, you may want to use the `--cautious` flag to disable the internal comment conversion.
```CoffeeScript
###
Move the animal.
@example Move an animal
new Lion('Simba').move('south', 12)
@param [Object] options the moving options
@option options [String] direction the moving direction
@option options [Number] speed the speed in mph
###
move: (options = {}) ->
```
If you want to compile your JavaScript with Google Closure and make use of the special block comments with an asterisk, you want to use the `--closure` flag so that Codo ignores the asterisk.
### Parameters
There are two different format recognized for your parameters, so you can chose your favorite. This one is with the
parameter after the parameter type:
```CoffeeScript
# Feed the animal
#
# @param [World.Food] food the food to eat
# @param [Object] options the feeding options
# @option options [String] time the time to feed
#
feed: (food) ->
```
And this one with the name before the type:
```CoffeeScript
# Feed the animal
#
# @param food [World.Food] the food to eat
# @param options [Object] the feeding options
# @option options time [String] the time to feed
#
feed: (food) ->
```
The parameter type can contain multiple comma separated types:
```CoffeeScript
# Feed the animal
#
# @param [String, Char] input
# @return [Integer, Float] output
#
do: (input) ->
```
Each known type will be automatically linked. Also named parameters are recognized:
```CoffeeScript
class Classmate
# @param {string} name Full name (first + last)
# @param {string} phone Phone number
# @param {obj} picture JPG of the person
constructor: ( {@name, @phone, picture} ) ->
# @param {string} reason Why I'm no longer friends
# @param {Date} revisit_decision_on When to reconsider
unfriend: ( {reason, revisit_decision_on} ) ->
```
### Options
If you have an object as parameter and you like to define the accepted properties as options to the method, you can use the `@options` tag:
```CoffeeScript
# Feed the animal
#
# @param [Object] options the calculation options
# @option options [Integer] age the age of the animal
# @option options [Integer] weight the weight of the animal
#
expectationOfLife: (options) ->
```
The first parameter to the option tag is the parameter name it describes, followed by the parameter type, name and
description.
### Types
The object types for the `@param`, `@option` and `@return` tags are parsed for known classes or mixins and linked. You can also define types for Arrays with:
```CoffeeScript
#
# @param [World.Region] region the region of the herd
# @return [Array] the animals in the herd
#
getHerdMembers: (regions) ->
```
### Properties
You can mark an instance variable as property of the class by using the `@property` tag like:
```CoffeeScript
class Person
# @property [Array] the nicknames
nicknames: []
```
In addition, the following properties pattern is detected:
```CoffeeScript
class Person
get = (props) => @::__defineGetter__ name, getter for name, getter of props
set = (props) => @::__defineSetter__ name, setter for name, setter of props
# @property [String] The person name
get name: -> @_name
set name: (@_name) ->
# The persons age
get age: -> @_age
```
If you follow this convention, they will be shown in the generated documentation with its read/write status shown. To specify type of the property, use the `@property` tag.
### Method overloading
If you allow your method to take different parameters, you can describe the method overloading with the `@overload` tag:
```CoffeeScript
# This is a generic Store set method.
#
# @overload set(key, value)
# Sets a value on key
# @param [Symbol] key describe key param
# @param [Object] value describe value param
#
# @overload set(value)
# Sets a value on the default key `:foo`
# @param [Object] value describe value param
# @return [Boolean] true when success
#
set: (args...) ->
```
The `@overload` tag must be followed by the alternative method signature that will appear in the documentation, followed by any method tag indented by two spaces.
### Virtual methods
If you copy over functions from other objects without using mixins or concerns, you can add documentation for this
virtual (or dynamic) method with the `@method` tag:
```CoffeeScript
# This class has a virtual method, that doesn't
# exist in the source but appears in the documentation.
#
# @method #set(key, value)
# Sets a value on key
# @param [Symbol] key describe key param
# @param [Object] value describe value param
#
class VirtualMethods
```
The `@method` tag must be followed by the method signature that will appear in the documentation, followed
by any method tag indented by two spaces. The difference to the `@overload` tag beside the different context is that the signature should contain either the instance prefix `#` or the class prefix `.`.
### Mixins
It's common practice to mix in objects to share common logic when inheritance is not suited. You can read
more about mixins in the [The Little Book on CoffeeScript](http://arcturo.github.io/library/coffeescript/03_classes.html).
Simply mark any plain CoffeeScript object with the `@mixin` tag to have a mixin page generated that supports many tags:
```CoffeeScript
# Speed calculation for animal.
#
# @mixin
# @author Rockstar Ninja
#
Example.Animal.Speed =
# Get the distance the animal will put back in a certain time.
#
# @param [Integer] time Number of seconds
# @return [Integer] The distance in miles
#
distance: (time) ->
```
Next mark the target object that includes one or multiple mixins:
```CoffeeScript
# @include Example.Animal.Speed
class Example.Animal.Lion
```
and you'll see the mixin methods appear as instance methods in the lion class documentation. You can also extend a mixin:
```CoffeeScript
# @extend Example.Animal.Speed
class Example.Animal.Lion
```
so its methods will show up as class methods.
#### Concerns
A concern is a combination of two mixins, one for instance methods and the other for class methods and it's automatically detected when a mixin has both a `ClassMethods` and an `InstanceMethods` property:
```CoffeeScript
# Speed calculations for animal.
#
# @mixin
# @author Rockstar Ninja
#
Example.Animal.Speed =
InstanceMethods:
# Get the distance the animal will put back in a certain time.
#
# @param [Integer] time Number of seconds
# @return [Integer] The distance in miles
#
distance: (time) ->
ClassMethods:
# Get the common speed of the animal in MPH.
#
# @param [Integer] age The age of the animal
# @return [Integer] The speed in MPH
#
speed: (age) ->
```
You can use `@concern` to include and extend the correspondent properties:
```CoffeeScript
# @concern Example.Animal.Speed
class Example.Animal.Lion
```
### Non-class methods and variables
You can also document your non-class, top level functions and constants within a file. As soon Codo detects these types within a file, it will be added to the file list and you can browse your file methods and constants.
## Text processing
### GitHub Flavored Markdown
Codo class, mixin and method documentation and extra files written in
[Markdown](http://daringfireball.net/projects/markdown/) syntax are rendered as full
[GitHub Flavored Markdown](https://github.github.com/github-flavored-markdown/).
The `@return`, `@param`, `@option`, `@see`, `@author`, `@copyright`, `@note`, `@todo`, `@since`, `@version` and
`@deprecated` tags rendered with a limited Markdown syntax, which means that only inline elements will be returned.
### Automatically link references
Codo comments and all tag texts will be parsed for references to other classes, methods and mixins, and are automatically linked. The reference searching will not take place within code blocks, thus you can avoid reference searching errors by surround your code block that contains curly braces with backticks.
There are several ways of link types supported and all can take an optional label after the link.
* Normal URL links: `{http://coffeescript.org/}` or `{http://coffeescript.org/ Try CoffeeScript}`
* Link to a class or mixin: `{Animal.Lion}` or `{Animal.Lion The mighty lion}`
* Direct link to an instance method: `{Animal.Lion#walk}` or `{Animal.Lion#walk The lion walks}`
* Direct link to a class method: `{Animal.Lion.constructor}` or `{Animal.Lion.constructor A new king was born}`
* Direct link to a module method: `{MyModule~method}` or `{MyModule~method ZOMG I can even refer modules!}`
The `@see` tag supports the same link types, just without the curly braces:
```CoffeeScript
@see https://en.wikipedia.org/wiki/Lion The wikipedia page about lions
```
## Generate
After the installation you will have a `codo` binary that can be used to generate the documentation recursively for all CoffeeScript files within a directory.
```bash
$ codo --help
Usage: codo [options] [source_files [- extra_files]]
Options:
--help, -h Show this help
--version Show version
--extension, -x Coffee files extension [default: "coffee"]
--output, -o The output directory [default: "./doc"]
--min-coverage, -m Require a minimum percentage to be documented or fail [default: 0]
--test, -t Do not create any output files. Use with min-coverage [default: 0]
--theme The theme to be used [default: "default"]
--name, -n The project name used
--readme, -r The readme file used
--quiet, -q Supress warnings [default: false]
--verbose, -v Show parsing errors [default: false]
--undocumented, -u List undocumented objects [default: false]
--closure Try to parse closure-like block comments [default: false]
--private, -p Show privates [default: false]
--analytics, -a The Google analytics ID [default: false]
--title, -t HTML Title [default: "Codo Documentation"]
```
Codo wants to be smart and tries to detect the best default settings for the sources, the readme, the extra files and the project name, so the above defaults may be different on your project.
### Project defaults
You can define your project defaults by writing your command line options to a `.codoopts` file:
```bash
--name "Codo"
--readme README.md
--title "Codo Documentation"
--private
--quiet
--extension coffee
--output ./doc
./src
-
LICENSE
CHANGELOG.md
```
Put each option flag on a separate line, followed by the source directories or files, and optionally any extra file that should be included into the documentation separated by a dash (`-`). If your extra file has the extension `.md`, it'll be rendered as Markdown.
### API usage
If you want to use codo in your build tool, you can require `codo/lib/command.coffee`:
```coffeescript
CodoCLI = require 'codo/lib/command.coffee'
codoCLI = new CodoCLI()
codoCLI.generate "path/to/base/dir", options, (exitCode) -> process.exit exitCode
```
`option` is an object with options as above. Please note that they are *not* CamelCase (e.g. `min-coverage`
instead of `minCoverage`). Furthermore only global defaults will be used, project defaults are ignored
if Codo is used via API.
## Keyboard navigation
You can quickly search and jump through the documentation by using the fuzzy finder dialog:
* Open fuzzy finder dialog: `T`
In frame mode you can toggle the list naviation frame on the left side:
* Toggle list view: `L`
You can focus a list in frame mode or toggle a tab in frameless mode:
* Class list: `C`
* Mixin list: `I`
* File list: `F`
* Method list: `M`
* Extras list: `E`
You can focus and blur the search input:
* Focus search input: `S`
* Blur search input: `Esc`
In frameless mode you can close the list tab:
* Close list tab: `Esc`
## Report issues
Issues hosted at [GitHub Issues](https://github.com/coffeedoc/codo/issues).
The Codo specs are template based, so make sure you provide a code snippet that can be added as failing spec to the
project when reporting an issue with parsing your CoffeeScript code. The other thing that might be useful is the actual exception happening (run with `-d`).
## Development
Source hosted at [GitHub](https://github.com/coffeedoc/codo).
Pull requests are very welcome! Please try to follow these simple rules if applicable:
* Please create a topic branch for every separate change you make.
* Make sure your patches are well tested.
* Update the documentation.
* Update the README.
* Update the CHANGELOG for noteworthy changes.
* Please **do not change** the version number.
## Alternatives
* [Docco](http://jashkenas.github.io/docco/) is a quick-and-dirty, literate-programming-style documentation generator.
* [CoffeeDoc](https://github.com/omarkhan/coffeedoc) an alternative API documentation generator for CoffeeScript.
* [JsDoc](https://github.com/micmath/jsdoc) an automatic documentation generator for JavaScript.
* [Dox](https://github.com/tj/dox) JavaScript documentation generator for node using markdown and jsdoc.
## Core Team
* [Boris Staal](https://github.com/inossidabile) ([@_inossidabile](http://twitter.com/#!/_inossidabile))
* [Michael Kessler](https://github.com/netzpirat) ([@netzpirat](http://twitter.com/#!/netzpirat), [FlinkFinger](http://www.blackbeans.ch))
## Acknowledgment
- [Jeremy Ashkenas](https://github.com/jashkenas) for [CoffeeScript](http://coffeescript.org/), that mighty language
that compiles to JavaScript and makes me enjoy JavaScript development.
- [Loren Segal](https://github.com/lsegal) for creating YARD and giving me the perfect documentation syntax for
dynamic programming languages.
- [Stratus Editor](https://github.com/stratuseditor) for open sourcing their [fuzzy filter](https://github.com/stratuseditor/fuzzy-filter).
## License
(The MIT License)
Copyright (c) 2012-2016 Michael Kessler, Boris Staal
Template components are derivative works of YARD (http://yardoc.org)
Copyright (c) Loren Segal and licensed under the MIT license
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.
[](https://bitdeli.com/free "Bitdeli Badge")
================================================
FILE: bin/codo
================================================
#!/usr/bin/env node
'use strict';
var compiler = require('coffee-script');
var location = require('path').join(
__dirname, '..', 'lib', 'command'
)
if (compiler.register) {
compiler.register();
}
require(location).run();
================================================
FILE: lib/_entities.coffee
================================================
module.exports = Entities = {}
================================================
FILE: lib/_meta.coffee
================================================
module.exports = Meta = {}
================================================
FILE: lib/_tools.coffee
================================================
module.exports = Tools = {}
================================================
FILE: lib/codo.coffee
================================================
FS = require 'fs'
Path = require 'path'
walkdir = require 'walkdir'
Winston = require 'winston'
module.exports = Codo =
Environment: require './environment'
Tools:
Markdown: require './tools/markdown'
Referencer: require './tools/referencer'
Entities:
File: require './entities/file'
Class: require './entities/class'
Method: require './entities/method'
Variable: require './entities/variable'
Property: require './entities/property'
Mixin: require './entities/mixin'
Extra: require './entities/extra'
Meta:
Method: require './meta/method'
Parameter: require './meta/parameter'
version: ->
JSON.parse(
FS.readFileSync(Path.join(__dirname, '..', 'package.json'), 'utf-8')
)['version']
parseProject: (path, options={}) ->
options.name ||= @detectName(path)
options.readme ||= @detectReadme(path)
options.basedir ||= path
options.extension ||= 'coffee'
environment = new @Environment(options)
if environment.options.readme
environment.readExtra(Path.join path, environment.options.readme)
for extra in (options.extras || @detectExtras(path))
environment.readExtra(Path.join path, extra)
for input in (options.inputs || [path])
if FS.existsSync(input)
if FS.lstatSync(input).isDirectory()
for filename in walkdir.sync(input) when filename.match("\\._?#{options.extension}$")
environment.readCoffee(filename)
else
environment.readCoffee(Path.resolve input)
else
Winston.warn("#{input} (#{Path.join process.cwd(), input}) skipped – does not exist")
environment.linkify()
environment
detectDefaults: (path) ->
results =
_: []
try
if FS.existsSync(Path.join path, '.codoopts')
configs = FS.readFileSync Path.join(path, '.codoopts'), 'utf8'
for config in configs.split('\n')
# Key value configs
if option = /^-{1,2}([\w-]+)\s+(['"])?(.*?)\2?$/.exec config
results[option[1]] = option[3]
# Boolean configs
else if bool = /^-{1,2}([\w-]+)\s*$/.exec config
results[bool[1]] = true
# Argv configs
else if config != ''
results._.push(config)
results
catch error
Winston.error("Cannot parse .codoopts file: #{error.message}") unless @quiet
# Find the project name by either parse `package.json`
# or get the current working directory name.
#
detectName: (path) ->
if FS.existsSync(Path.join path, 'package.json')
name = JSON.parse(FS.readFileSync Path.join(path, 'package.json'), 'utf-8')['name']
if !name && FS.existsSync(Path.join path, '.git', 'config')
config = FS.readFileSync(Path.join(path, '.git', 'config'), 'utf-8')
name = /github\.com[:/][^/]+\/(.*)\.git/.exec(config)?[1]
if !name
name = Path.basename(path)
return name.charAt(0).toUpperCase() + name.slice(1)
# Find the project README.
#
detectReadme: (path) ->
attempts = [
'README.markdown'
'README.md'
'README'
]
return attempt for attempt in attempts when FS.existsSync(Path.join path, attempt)
# Find extra project files.
#
detectExtras: (path) ->
[
'CHANGELOG'
'CHANGELOG.markdown'
'CHANGELOG.md'
'AUTHORS'
'AUTHORS.md'
'AUTHORS.markdown'
'LICENSE'
'LICENSE.md'
'LICENSE.markdown'
'LICENSE.MIT'
'LICENSE.GPL'
'README.markdown'
'README.md'
'README'
].filter (attempt) -> FS.existsSync(Path.join path, attempt)
================================================
FILE: lib/command.coffee
================================================
Path = require 'path'
Codo = require './codo'
Optimist = require 'optimist'
Theme = require '../themes/default/lib/theme'
Table = require 'cli-table'
colors = require 'colors/safe'
module.exports = class Command
options: [
{name: 'help', alias: 'h', describe: 'Show this help'}
{name: 'version', describe: 'Show version'}
{name: 'extension', alias: 'x', describe: 'Coffee files extension', default: 'coffee'}
{name: 'output', alias: 'o', describe: 'The output directory', default: './doc'}
{name: 'min-coverage', alias: 'm', describe: 'Require a minimum percentage to be documented or fail', default: '0'}
{name: 'output-dir'}
{name: 'theme', describe: 'The theme to be used', default: 'default'}
{name: 'name', alias: 'n', describe: 'The project name used'}
{name: 'readme', alias: 'r', describe: 'The readme file used'}
{name: 'quiet', alias: 'q', describe: 'Supress warnings', boolean: true, default: false}
{name: 'verbose', alias: 'v', describe: 'Show parsing errors', boolean: true, default: false}
{name: 'undocumented', alias: 'u', describe: 'List undocumented objects', boolean: true, default: false}
{name: 'closure', describe: 'Try to parse closure-like block comments', boolean: true, default: false}
{name: 'debug', alias: 'd', boolean: true}
]
@run: ->
new @().run (code) ->
process.exit code
extendOptimist: (optimist, defaults={}, options={}) ->
for option in options
optimist.options option.name,
alias: option.alias,
describe: option.describe,
boolean: option.boolean,
default: defaults[option.name] || defaults[option.alias] || option.default
lookupTheme: (name) ->
if name == 'default'
@theme = Theme
else
try
@theme = require "codo-theme-#{name}"
catch
try
@theme = require Path.resolve("node_modules/codo-theme-#{name}")
catch
console.log "Error loading theme #{name}: are you sure you have codo-theme-#{name} package installed?"
process.exit()
prepareOptions: (optimist, defaults) ->
options = optimist.argv
options._.push entry for entry in defaults._
keyword = 'inputs'
for entry in options._
if entry == '-'
keyword = 'extras'
else
options[keyword] ?= []
options[keyword].push entry
delete options._
options
run: (cb) ->
defaults = Codo.detectDefaults(process.cwd())
optimist = Optimist.usage('Usage: $0 [options] [source_files [- extra_files]]')
@extendOptimist(optimist, defaults, @options)
@theme = @lookupTheme(optimist.argv.theme)
@extendOptimist(optimist, defaults, @theme::options)
@options = @prepareOptions(optimist, defaults)
if @options['output-dir']
console.log "The usage of outdated `--output-dir` option detected. Please switch to `--output`."
process.exit()
if @options.help
console.log optimist.help()
else if @options.version
console.log Codo.version()
else
@generate(process.cwd(), @options, cb)
collectStats: (environment) ->
sections =
Classes:
total: environment.allClasses().length
undocumented: environment.allClasses().filter((e) -> !e.documentation?).map (x) ->
[x.name, x.file.path]
Mixins:
total: environment.allMixins().length
undocumented: environment.allMixins().filter((e) -> !e.documentation?).map (x) ->
[x.name, x.file.path]
Methods:
total: environment.allMethods().length
undocumented: environment.allMethods().filter((e) -> !e.entity.documentation?).map (x) ->
["#{x.entity.name} (#{x.owner.name})", x.owner.file.path]
sections
generate: (dir = process.cwd(), options = @options, cb) ->
for option in @options
if option.default?
options[option.name] ?= option.default
@theme ?= @lookupTheme(options.theme)
for option in @theme::options
if option.default?
options[option.name] ?= option.default
environment = Codo.parseProject(dir, options)
sections = @collectStats(environment)
unless options.test
@theme.compile(environment)
overall = 0
undocumented = 0
for section, data of sections
overall += data.total
undocumented += data.undocumented.length
if options.undocumented
for section, data of sections when data.undocumented.length != 0
table = new Table
head: [section, 'Path']
table.push(entry) for entry in data.undocumented
unless options.test
console.log table.toString()
console.log ''
else
table = new Table
head: ['', 'Total', 'Undocumented']
undocumented_percent = 100/overall*undocumented || 0
table.push(
['Files', environment.allFiles().length, ''],
['Extras', environment.allExtras().length, ''],
['Classes', sections['Classes'].total, sections['Classes'].undocumented.length],
['Mixins', sections['Mixins'].total, sections['Mixins'].undocumented.length],
['Methods', sections['Methods'].total, sections['Methods'].undocumented.length]
)
unless options.test
console.log table.toString()
console.log ''
console.log " Totally documented: #{(100 - undocumented_percent).toFixed(2)}%"
console.log ''
documentedRatio = 100 - (100*undocumented/overall).toFixed(2)
if documentedRatio < options["min-coverage"]
unless options.test
console.error colors.red(" Expected " + options["min-coverage"] +
"% to be documented, but only " + documentedRatio + "% were.")
cb 1 if cb
else
cb() if cb
================================================
FILE: lib/documentation.coffee
================================================
module.exports = class Documentation
constructor: (comment) ->
@parseTags(comment)
# Parse the given lines and adds the result
# to the result object.
#
# @param [Array] lines the lines to parse
#
parseTags: (lines) ->
comment = []
while (line = lines.shift()) isnt undefined
# Look ahead
unless /^@example|@overload|@method|@event/.exec line
while /^\s{2}\S+/.test(lines[0])
line += lines.shift().substring(1)
if property = /^@nodoc/i.exec line
@nodoc = true
if property = /^@property\s+[\[\{](.+?)[\]\}](?:\s+(.+))?/i.exec line
@property = property[1]
lines.unshift property[2] if property[2]?
else if returns = /^@return\s+[\[\{](.+?)[\]\}](?:\s+(.+))?/i.exec line
@returns =
type: returns[1]
description: returns[2]
else if returns = /^@return\s+(.+)/i.exec line
@returns =
type: '?'
description: returns[1]
else if throws = /^@throw\s+[\[\{](.+?)[\]\}](?:\s+(.+))?/i.exec line
@throws ?= []
@throws.push
type: throws[1]
description: throws[2]
else if throws = /^@throw\s+(.+)/i.exec line
@throws ?= []
@throws.push
type: '?'
description: throws[1]
else if param = /^@param\s+[\[\{](.+?)[\]}]\s+\[([^\]]+)](?:\s+(.+))?/i.exec line
@params ?= []
if paramNameVal = /^([^ ]+)\s*=\s*([^ ]+)/.exec param[2]
paramName = paramNameVal[1]
defValue = paramNameVal[2]
else
defValue = null
paramName = param[2]
@params.push
type: param[1]
name: paramName
description: param[3]
defaultState: defValue
optional: yes
else if param = /^@param\s+([^ ]+)\s+[\[\{](.+?)[\]\}](?:\s+(.+))?/i.exec line
@params ?= []
@params.push
type: param[2]
name: param[1]
description: param[3]
else if param = /^@param\s+[\[\{](.+?)[\]\}]\s+([^ ]+)(?:\s+(.+))?/i.exec line
@params ?= []
@params.push
type: param[1]
name: param[2]
description: param[3]
else if option = /^@option\s+([^ ]+)\s+[\[\{](.+?)[\]\}]\s+([^ ]+)(?:\s+(.+))?/i.exec line
@options ?= {}
@options[option[1]] ?= []
@options[option[1]].push
type: option[2]
name: option[3]
description: option[4]
else if option = /^@option\s+([^ ]+)\s+([^ ]+)\s+[\[\{](.+?)[\]\}](?:\s+(.+))?/i.exec line
@options ?= {}
@options[option[1]] ?= []
@options[option[1]].push
type: option[3]
name: option[2]
description: option[4]
else if see = /^@see\s+([^\s]+)(?:\s+(.+))?/i.exec line
@see ?= []
@see.push
reference: see[1]
label: see[2]
else if author = /^@author\s+(.+)/i.exec line
@authors ?= []
@authors.push author[1] || ''
else if copyright = /^@copyright\s+(.+)/i.exec line
@copyright = copyright[1] || ''
else if note = /^@note\s+(.+)/i.exec line
@notes ?= []
@notes.push note[1] || ''
else if todo = /^@todo\s+(.+)/i.exec line
@todos ?= []
@todos.push todo[1] || ''
else if example = /^@example(?:\s+(.+))?/i.exec line
title = example[1] || ''
code = []
while /^\s{2}.*/.test(lines[0]) or (/^$/.test(lines[0]) and /^\s{2}.*/.test(lines[1]))
code.push lines.shift().substring(2)
if code.length isnt 0
@examples ?= []
@examples.push
title: title
code: code.join '\n'
else if namespace = /^@namespace\s+(.+)/i.exec line
@namespace = namespace[1] || ''
else if abstract = /^@abstract(?:\s+(.+))?/i.exec line
@abstract = abstract[1] || ''
else if /^@private/.exec line
@private = true
else if /^@public/.exec line
@public = true
else if since = /^@since\s+(.+)/i.exec line
@since = since[1] || ''
else if version = /^@version\s+(.+)/i.exec line
@version = version[1] || ''
else if deprecated = /^@deprecated(\s+)?(.*)/i.exec line
@deprecated = deprecated[2] || ''
else if mixin = /^@mixin/i.exec line
@mixin = true
else if concern = /^@concern\s+(.+)/i.exec line
@concerns ?= []
@concerns.push concern[1]
else if include = /^@include\s+(.+)/i.exec line
@includes ?= []
@includes.push include[1]
else if extend = /^@extend\s+(.+)/i.exec line
@extends ?= []
@extends.push extend[1]
else if event = /^@event\s+(\S+)(\s+(.+))?/i.exec line
@events ?= []
innerComment = []
innerComment.push(event[2]) if event[2]
doc = {}
while /^\s{2}.*/.test(lines[0]) || /^\s*$/.test(lines[0])
innerComment.push lines.shift().substring(2)
@parseTags.call(doc, innerComment) if innerComment
@events.push
name: event[1]
documentation: doc
else if overload = /^@overload\s+(.+)/i.exec line
signature = overload[1]
innerComment = []
while /^\s{2}.*/.test(lines[0]) || /^\s*$/.test(lines[0])
innerComment.push lines.shift().substring(2)
if innerComment.length != 0
@overloads ?= []
doc = {}
@parseTags.call doc, innerComment
@overloads.push
signature: signature
documentation: doc
else if method = /^@method\s+(.+)/i.exec line
signature = method[1]
innerComment = []
while /^\s{2}.*/.test(lines[0]) or /^\s*$/.test(lines[0])
innerComment.push lines.shift().substring(2)
if innerComment.length isnt 0
@methods ?= []
doc = {}
@parseTags.call doc, innerComment
@methods.push
signature: signature
documentation: doc
else
comment.push line
text = comment.join('\n')
@comment = text.trim()
sentence = /((?:.|\n)*?[.#][\s$])/.exec(text)
sentence = sentence[1].replace(/\s*#\s*$/, '') if sentence
@summary = (sentence || text || '').trim()
inspect: ->
{
comment: @comment
summary: @summary
notes: @notes
see: @see
namespace: @namespace
abstract: @abstract
private: @private
public: @public
deprecated: @deprecated
version: @version
since: @since
authors: @authors
copyright: @copyright
todos: @todos
includes: @includes
extends: @extends
concerns: @concerns
examples: @examples
params: @params
options: @options
returns: @returns
throws: @throws
overloads: @overloads
events: @events
methods: @methods
property: @property
}
================================================
FILE: lib/entities/class.coffee
================================================
Entity = require '../entity'
Method = require './method'
Variable = require './variable'
Property = require './property'
Mixin = require './mixin'
MetaMethod = require '../meta/method'
Entities = require '../_entities'
Winston = require 'winston'
module.exports = class Entities.Class extends Entity
@name = "Class"
@looksLike: (node) ->
node.constructor.name is 'Class' && node.variable?.base?.value?
constructor: (@environment, @file, @node) ->
[@selfish, @container] = @determineContainment(@node.variable)
@parent = @fetchParent(@node.parent) if @node.parent
@documentation = @node.documentation
@name = @fetchName(@node.variable, @selfish, @container)
@methods = []
@variables = []
@properties = []
@includes = []
@extends = []
@concerns = []
@descendants = []
name = @name.split('.')
@basename = name.pop()
@namespace = @documentation?.namespace or name.join('.')
if @environment.options.debug
Winston.info "Creating new Class Entity"
Winston.info " name: " + @name
Winston.info " documentation: " + @documentation
@
# Determines if the class definition at given node is using @assignation
# and if in such case this class is nested into another one
determineContainment: (node) ->
if node.base?.value == 'this'
selfish = true # class @Foo
container = @lookup(Class, node) # class Foo \n class @Bar
[selfish, container]
fetchParent: (source) ->
[selfish, container] = @determineContainment(source)
@fetchName(source, selfish, container)
fetchName: (source, selfish, container) ->
name = []
# Nested class definition inherits
# the namespace from the containing class
name.push container.name if container
# Take the actual name of assignation unless
# we are prefixed with `@`
name.push source.base.value if !selfish && source.base?
# Get the rest of actual assignation path
if source.properties
name.push prop.name.value for prop in source.properties when prop.name?
# Here comes the magic!
name.join('.')
linkify: ->
super
for node in @node.body.expressions
if node.constructor.name == 'Assign' && node.entities?
@linkifyAssign(node)
if node.constructor.name == 'Value'
@linkifyValue(node)
if node.constructor.name == 'Call' && node.entities?
@linkifyCall(node)
@linkifyParent()
@linkifyMixins()
linkifyAssign: (node) ->
for entity in node.entities when entity.selfish
# class Foo
# @foo = ->
if entity instanceof Method
entity.kind = 'static'
@methods.push entity
# class Foo
# @foo = 'test'
if entity instanceof Variable
entity.kind = 'static'
@variables.push entity
linkifyValue: (node) ->
for property in node.base.properties when property.entities?
for entity in property.entities
# class Foo
# @foo: ->
# foo: ->
if entity instanceof Method
entity.kind = if entity.selfish then 'static' else 'dynamic'
@methods.push entity
# class Foo
# foo: 'test'
if entity instanceof Variable
entity.kind = if entity.selfish then 'static' else 'dynamic'
@variables.push entity
if entity instanceof Property
@properties.push entity
linkifyCall: (node) ->
for entity in node.entities
if entity instanceof Property
found = false
for property in @properties
if property.name == entity.name
entity.unite(property)
found = true
@properties.push(entity) unless found
linkifyParent: ->
if @parent
@parent = @environment.find(Class, @parent) || @parent
@parent.descendants?.push(@)
linkifyMixins: ->
if @documentation?.includes?
for entry in @documentation.includes
mixin = @environment.find(Mixin, entry) || entry
@includes.push(mixin)
mixin.inclusions?.push(@)
if @documentation?.extends?
for entry in @documentation.extends
mixin = @environment.find(Mixin, entry) || entry
@extends.push(mixin)
mixin.extensions?.push(@)
if @documentation?.concerns?
for entry in @documentation.concerns
mixin = @environment.find(Mixin, entry) || entry
@concerns.push(mixin)
mixin.concerns?.push(@)
effectiveMethods: ->
return @_effectiveMethods if @_effectiveMethods?
@_effectiveMethods = []
for method in @methods
@_effectiveMethods.push(MetaMethod.fromMethodEntity method)
if @documentation?.methods
for method in @documentation.methods
@_effectiveMethods.push(MetaMethod.fromDocumentationMethod method)
@_effectiveMethods
allMethods: ->
methods = @effectiveMethods().map (method) =>
{
entity: method
owner: @
}
resolvers =
includes: 'effectiveInclusionMethods'
extends: 'effectiveExtensionMethods'
concerns: 'effectiveConcernMethods'
for storage, resolver of resolvers
for mixin in @[storage]
if mixin[resolver]
for method in mixin[resolver]()
methods.push
entity: method
owner: mixin
methods
inherited: (getter) ->
return [] if !@parent || !@parent.name?
found = {}
entries = getter()
entries.filter (entry) ->
found[entry.entity.name] = true unless found[entry.entity.name]
inheritedMethods: ->
@_inheritedMethods ||= @inherited =>
@parent.allMethods().concat @parent.inheritedMethods()
inheritedVariables: ->
@_inheritedVariables ||= @inherited =>
variables = @parent.variables.map (variable) =>
{
entity: variable
owner: @parent
}
variables.concat @parent.inheritedVariables()
inheritedProperties: ->
@_inheritedProperties ||= @inherited =>
properties = @parent.properties.map (property) =>
{
entity: property
owner: @parent
}
properties.concat @parent.inheritedProperties()
inspect: ->
{
file: @file.path
documentation: @documentation?.inspect()
selfish: @selfish
name: @name
container: @container?.inspect()
parent: @parent?.inspect?() || @parent
methods: @methods.map (x) -> x.inspect()
variables: @variables.map (x) -> x.inspect()
properties: @properties.map (x) -> x.inspect()
includes: @includes.map (x) -> x.inspect?() || x
extends: @extends.map (x) -> x.inspect?() || x
concerns: @concerns.map (x) -> x.inspect?() || x
}
================================================
FILE: lib/entities/extra.coffee
================================================
FS = require 'fs'
Path = require 'path'
Entities = require '../_entities'
Markdown = require '../tools/markdown'
module.exports = class Entities.Extra
@name: "Extra"
constructor: (@environment, @path) ->
@name = Path.relative(@environment.options.basedir, @path)
@content = FS.readFileSync @path, 'utf-8'
if @environment.options.debug
Winston.info "Creating new Extra Entity"
Winston.info " name: " + @name
Winston.info " content: " + @content
@parsed = if /\.(markdown|md)$/.test @path
Markdown.convert(@content)
else
"
"+@content.replace(/\n/g, ' ')+"
"
linkify: ->
inspect: ->
{
path: @path,
parsed: @parsed
}
================================================
FILE: lib/entities/file.coffee
================================================
Entity = require '../entity'
Path = require 'path'
Method = require './method'
Variable = require './variable'
Mixin = require './mixin'
Class = require './class'
MetaMethod = require '../meta/method'
Entities = require '../_entities'
Winston = require 'winston'
module.exports = class Entities.File extends Entity
@Name: "File"
constructor: (@environment, @path, @node) ->
@file = @
@name = Path.relative(@environment.options.basedir, @path)
@basename = Path.basename(@name)
@dirname = Path.dirname(@name)
@methods = []
@variables = []
@mixins = []
@classes = []
if @environment.options.debug
Winston.info "Creating new File Entity"
Winston.info " name: " + @name
Winston.info " path: " + @path
linkify: ->
super
for node in @node.expressions
# Checking direct members
unless entities = node.entities
# And members prefixed with `module.exports =`
if node.variable?.base?.value == 'module'
if node.variable?.properties?[0]?.name?.value == 'exports'
entities = node.value?.entities
if entities
for entity in entities
if entity instanceof Method
@methods.push(entity) if entity.name.length > 0
if entity instanceof Variable
@variables.push entity
if entity instanceof Mixin
@mixins.push entity
if entity instanceof Class
@classes.push entity
effectiveMethods: ->
@_effectiveMethods ||= @methods.map (method) -> MetaMethod.fromMethodEntity method
inspect: ->
{
file: @name
methods: @methods.map (x) -> x.inspect()
variables: @variables.map (x) -> x.inspect()
}
================================================
FILE: lib/entities/method.coffee
================================================
Entity = require '../entity'
Parameter = require '../meta/parameter'
Entities = require '../_entities'
Winston = require 'winston'
module.exports = class Entities.Method extends Entity
@name: "Method"
@looksLike: (node) ->
node.constructor.name == 'Assign' && node.value?.constructor.name == 'Code'
constructor: (@environment, @file, @node) ->
Winston.info "Creating new Method Entity" if @environment.options.debug
@name = [@node.variable.base.value]
@name.push prop.name.value for prop in @node.variable.properties when prop.name?
if @name[0] == 'this'
@selfish = true
@name = @name.slice(1)
if @name[0] == 'module' && @name[1] == 'exports'
@name = @name.slice(2)
if @name[0] == 'exports'
@name = @name.slice(1)
@name = @name.join('.')
@bound = @node.value.bound
@documentation = @node.documentation
@parameters = @node.value.params.map (node) ->
Parameter.fromNode(node)
if @environment.options.debug
Winston.info " name: " + @name
Winston.info " documentation: " + @documentation
inspect: ->
{
file: @file.path
name: @name
bound: @bound
documentation: @documentation?.inspect()
selfish: @selfish
kind: @kind
parameters: @parameters.map (x) -> x.inspect()
}
================================================
FILE: lib/entities/mixin.coffee
================================================
Entity = require '../entity'
Method = require './method'
Variable = require './variable'
MetaMethod = require '../meta/method'
Entities = require '../_entities'
Winston = require 'winston'
module.exports = class Entities.Mixin extends Entity
@name: "Mixin"
@looksLike: (node) ->
node.constructor.name == 'Assign' && node.value?.base?.properties?
@is: (node) ->
node.documentation?.mixin && super(node)
@isConcernSection: (node) ->
node.constructor.name == 'Assign' &&
node.value?.constructor.name == 'Value' &&
(
node.variable.base.value == 'ClassMethods' ||
node.variable.base.value == 'InstanceMethods'
)
constructor: (@environment, @file, @node) ->
[@name, @selfish] = @fetchName()
@documentation = @node.documentation
@methods = []
@variables = []
@inclusions = []
@extensions = []
@concerns = []
for property in @node.value.base.properties
# Recognize assigned code on the mixin
@concern = true if @constructor.isConcernSection(property)
if @concern
@classMethods = []
@instanceMethods = []
name = @name.split('.')
@basename = name.pop()
@namespace = @documentation?.namespace or name.join('.')
if @environment.options.debug
Winston.info "Creating new Mixin Entity"
Winston.info " name: " + @name
Winston.info " documentation: " + @documentation
linkify: ->
super
@grabMethods @methods, @node
if @concern
for property in @node.value.base.properties
# Recognize concerns as inner mixins
if property.value?.constructor.name is 'Value'
switch property.variable.base.value
when 'ClassMethods'
@grabMethods @classMethods, property
when 'InstanceMethods'
@grabMethods @instanceMethods, property
grabMethods: (container, node) ->
for property in node.value.base.properties
if property.entities?
for entity in property.entities
# Foo =
# foo: ->
container.push entity if entity instanceof Method
aggregateEffectiveMethods: (kind) ->
methods = []
overrides = {}
overrides.kind = kind if kind?
for method in @methods
methods.push(MetaMethod.fromMethodEntity method, overrides)
if @documentation.methods
for method in @documentation.methods
methods.push(MetaMethod.fromDocumentationMethod method, overrides)
methods
effectiveMethods: ->
return @effectiveConcernMethods() if @concern
@_effectiveMethods ||= @aggregateEffectiveMethods()
effectiveInclusionMethods: ->
@_effectiveInclusionMethods ||= @aggregateEffectiveMethods('dynamic')
effectiveExtensionMethods: ->
@_effectiveExtensionMethods ||= @aggregateEffectiveMethods('static')
effectiveConcernMethods: ->
return @_effectiveConcernMethods if @_effectiveConcernMethods?
@_effectiveConcernMethods = []
for method in @classMethods
@_effectiveConcernMethods.push(MetaMethod.fromMethodEntity method, kind: 'static')
for method in @instanceMethods
@_effectiveConcernMethods.push(MetaMethod.fromMethodEntity method, kind: 'dynamic')
if @documentation.methods
for method in @documentation.methods
@_effectiveConcernMethods.push(MetaMethod.fromDocumentationMethod method)
@_effectiveConcernMethods
inspect: ->
{
file: @file.path
name: @name
concern: @concern
documentation: @documentation?.inspect()
selfish: @selfish
methods: @methods.map (x) -> x.inspect()
classMethods: @classMethods?.map (x) -> x.inspect()
instanceMethods: @instanceMethods?.map (x) -> x.inspect()
variables: @variables.map (x) -> x.inspect()
}
================================================
FILE: lib/entities/property.coffee
================================================
Entity = require '../entity'
Entities = require '../_entities'
Winston = require 'winston'
#
# Supported formats:
#
# foo: []
#
# get foo: ->
# set foo: (value) ->
#
# @property 'foo'
# @property 'foo', ->
# @property 'foo',
# get: ->
# set: (value) ->
#
module.exports = class Entities.Property extends Entity
@name: "Property"
@looksLike: (node) ->
(node.constructor.name == 'Assign' && node.value?.constructor.name == 'Value') ||
(node.constructor.name == 'Call' && node.variable?.base?.value == 'this') ||
(
node.constructor.name == 'Call' &&
node.args?[0]?.base?.properties?[0]?.variable?.base?.value &&
(node.variable?.base?.value == 'set' || node.variable?.base?.value == 'get')
)
@is: (node) ->
super(node) && (
node.documentation?.property ||
(node.constructor.name == 'Call' && node.variable?.base?.value != 'this')
)
constructor: (@environment, @file, @node) ->
if @node.constructor.name == 'Call' && @node.variable?.base?.value != 'this'
@name = @node.args[0].base.properties[0].variable.base.value
@setter = @node.variable.base.value == 'set'
@getter = @node.variable.base.value == 'get'
else if @node.constructor.name == 'Call' && @node.variable?.base?.value == 'this'
@name = @node.args[0].base.value.replace(/["']/g, '')
if @node.args.length > 1
if @node.args[1].constructor.name == 'Value'
# @property 'test', {set: ->, get: ->}
@setter = false
@getter = false
for property in @node.args[1].base?.properties
@setter = true if property.variable?.base.value == 'set'
@getter = true if property.variable?.base.value == 'get'
else
# @property 'test', ->
@setter = false
@getter = true
else
# @property 'test'
@setter = true
@getter = true
else
[@name, @selfish] = @fetchVariableName()
@setter = true
@getter = true
@documentation = @node.documentation
if @environment.options.debug
Winston.info "Creating new Property Entity"
Winston.info " name: " + @name
Winston.info " documentation: " + @documentation
fetchVariableName: ->
@fetchName()
unite: (property) ->
for attribute in ['documentation', 'getter', 'setter']
property[attribute] = @[attribute] = property[attribute] || @[attribute]
inspect: ->
{
file: @file.path
name: @name
getter: @getter
setter: @setter
documentation: @documentation?.inspect()
}
================================================
FILE: lib/entities/variable.coffee
================================================
Entity = require '../entity'
Entities = require '../_entities'
Winston = require 'winston'
module.exports = class Entities.Variable extends Entity
@name: "Variable"
@looksLike: (node) ->
node.constructor.name == 'Assign' && node.value?.constructor.name == 'Value' && node.variable?.base?.value? && node.value.base.constructor.name isnt 'Call'
@is: (node) ->
!node.documentation?.property && !node.documentation?.mixin && super(node)
constructor: (@environment, @file, @node) ->
[@name, @selfish] = @fetchName()
@constant = /^[A-Z_-]*$/.test @name
try
@value = @node.value.base.compile
indent: ''
# Workaround to replace CoffeeScript internal
# representations with something reasonable
@value = 'undefined' if @value == 'void 0'
@documentation = @node.documentation
if @environment.options.debug
Winston.info "Creating new Variable Entity"
Winston.info " name: " + @name
Winston.info " documentation: " + @documentation
inspect: ->
{
file: @file.path
name: @name
constant: @constant
value: @value
documentation: @documentation?.inspect()
selfish: @selfish
kind: @kind
}
================================================
FILE: lib/entity.coffee
================================================
# Base class for all entities.
#
module.exports = class Entity
@is: (node) ->
!node.documentation?.nodoc
linkify: ->
visible: ->
@environment.options.private || !@node.documentation?.private
fetchName: ->
name = [@node.variable.base.value]
name.push prop.name.value for prop in @node.variable.properties when prop.name?
if name[0] == 'this'
selfish = true
name = name.slice(1)
[name.join('.'), selfish]
lookup: (Entity, node) ->
if node.ancestor
if node.ancestor.entities?
for entity in node.ancestor.entities
return entity if entity instanceof Entity
@lookup Entity, node.ancestor
================================================
FILE: lib/environment.coffee
================================================
FS = require 'fs'
Path = require 'path'
Traverser = require './traverser'
File = require './entities/file'
Class = require './entities/class'
Method = require './entities/method'
Variable = require './entities/variable'
Property = require './entities/property'
Mixin = require './entities/mixin'
Extra = require './entities/extra'
walkdir = require 'walkdir'
Winston = require 'winston'
module.exports = class Environment
@read: (files, options={}) ->
files = [files] unless Array.isArray(files)
environment = new @(options)
environment.readCoffee(file) for file in files
environment.linkify()
environment
constructor: (@options={}) ->
@version = JSON.parse(
FS.readFileSync(Path.join(__dirname, '..', 'package.json'), 'utf-8')
)['version']
@options.name ?= 'Unknown Project'
@options.verbose ?= false
@options.debug ?= false
@options.cautios ?= false
@options.quiet ?= false
@options.closure ?= false
@options.output ?= 'doc'
@options.basedir ?= process.cwd()
@needles = []
@entities = []
@references = {}
@parsed = {}
@needles.push Class
@needles.push Method
@needles.push Variable
@needles.push Property
@needles.push Mixin
readCoffee: (file) ->
return if @parsed[file]
Winston.info("Parsing Codo file #{file}") if @options.verbose
try
Traverser.read(file, @)
catch error
throw error if @options.debug
Winston.error("Cannot parse Coffee file #{file}: #{error.message}") unless @options.quiet
finally
@parsed[file] = true
readExtra: (file) ->
return if @parsed[file]
Winston.info("Parsing Extra file #{file}") if @options.verbose
try
@registerEntity(new Extra @, file)
catch error
throw error if @options.debug
Winston.error("Cannot parse Extra file #{file}: #{error.message}") unless @options.quiet
finally
@parsed[file] = true
registerEntity: (entity) ->
@entities.push entity
all: (Entity, haystack = []) ->
for entity in @entities
haystack.push(entity) if entity instanceof Entity
haystack
visibleFiles: -> @allFiles()
visibleClasses: -> @allClasses().filter((x) -> x.visible())
visibleMixins: -> @allMixins().filter((x) -> x.visible())
visibleExtras: -> @allExtras()
visibleMethods: -> @allMethods().filter((x) -> x.entity.visible && x.owner.visible())
visibleVariables: -> @allVariables()
allFiles: -> @_allFiles ||= @all(File)
allClasses: -> @_allClasses ||= @all(Class)
allMixins: -> @_allMixins ||= @all(Mixin)
allExtras: -> @_allExtras ||= @all(Extra)
allMethods: ->
return @_allMethods if @_allMethods?
@_allMethods = []
for source in [@allFiles(), @allClasses(), @allMixins()]
for entry in source
for method in entry.effectiveMethods()
@_allMethods.push {entity: method, owner: entry}
@_allMethods.sort (a, b) ->
return -1 if a.entity.name < b.entity.name
return 1 if a.entity.name > b.entity.name
return 0
allVariables: ->
return @_allVariables if @_allVariables?
@_allVariables = []
for source in [@allFiles(), @allClasses(), @allMixins()]
for entry in source
for variable in entry.variables
@_allVariables.push {entity: variable, owner: entry}
@_allVariables
find: (Entity, name) ->
for entity in @entities
if entity instanceof Entity && entity.name == name
return entity
findReadme: ->
@find Extra, Path.relative(@options.basedir, @options.readme)
linkify: ->
entity.linkify() for entity in @entities
for basics in [@allFiles(), @allClasses(), @allMixins()]
for basic in basics
@references[basic.name] = basic
for variable in @allVariables()
keyword = variable.owner.name + '.' + variable.entity.name
@references[keyword] = variable
for method in @allMethods()
keyword = method.owner.name + method.entity.shortSignature()
@references[keyword] = method
reference: (needle, context='') ->
needle = needle.split(' ')[0]
if @references[needle]
@references[needle]
else if @references[context+needle]
@references[context+needle]
else
needle
inspect: ->
@entities.map (entity) -> entity.inspect()
================================================
FILE: lib/meta/method.coffee
================================================
Parameter = require './parameter'
Meta = require '../_meta'
module.exports = class Meta.Method
@override: (options, overrides) ->
options[key] = value for key, value of overrides
options
@fromMethodEntity: (entity, overrides={}) ->
options =
name: entity.name.match(/[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*/)?[0]
kind: entity.kind || ''
bound: entity.bound
parameters: entity.parameters.map (x) -> x.toString()
visible: entity.visible()
documentation: entity.documentation
new @(@override options, overrides)
@fromDocumentationMethod: (entry, overrides={}) ->
kind = switch entry.signature[0]
when '#'
'dynamic'
when '.'
'static'
options =
name: entry.signature.replace /^[\#\.]?["']?([^\("']+)\(.+/, '$1'
kind: kind || ''
parameters: Parameter.fromSignature(entry.signature).map (x) -> x.toString()
documentation: entry.documentation
visible: true # force this to be always visible
new @(@override options, overrides)
constructor: (options={}) ->
@constructor.override @, options
effectiveOverloads: ->
return @_effectiveOverloads if @_effectiveOverloads?
@_effectiveOverloads = []
if @documentation?.overloads
for overload in @documentation.overloads
@_effectiveOverloads.push(Method.fromDocumentationMethod overload)
else
@_effectiveOverloads.push(@)
@_effectiveOverloads
kindSignature: ->
switch @kind
when 'dynamic'
'#'
when 'static'
'.'
else
'~'
shortSignature: ->
@kindSignature() + @name
typeSignature: ->
'('+(@documentation?.returns?.type || 'void')+')'
paramsSignature: ->
'('+@parameters.join(', ')+')'
inspect: ->
{
name: @name,
kind: @kind,
bound: @bound,
parameters: @parameters
}
================================================
FILE: lib/meta/parameter.coffee
================================================
CoffeeScript = require 'coffee-script'
Meta = require '../_meta'
module.exports = class Meta.Parameter
@fromNode: (node) ->
new @ @fetchName(node), !!node.splat, @fetchDefault(node)
@fromSignature: (signature) ->
signature = signature.replace /^.([^\(]+)/, "x="
nodes = CoffeeScript.nodes("#{signature} ->").expressions[0].value.params
nodes.map (node) => @fromNode(node)
@fetchName: (node) ->
# Normal attribute `do: (it) ->`
name = node.name.value
# Named parameters a la python:
# `make_fac : ({numerator, divisor}) ->`
# Also works for class constructors:
# `constructor : ( { @name, @key, opts }) ->
unless name
if (o = node.name.objects)?
vars = for v in o
if v.base
if v.base.value is 'this' then v.properties[0].name.value
else v.base.value
else if v.variable && v.value
"#{v.variable.base.value}:#{v.value.base.value}"
else throw new Error('Unhandled syntax')
name = "{#{vars.join ', '}}"
# Assigned attributes `do: (@it) ->`
unless name
if node.name.properties
name = node.name.properties[0]?.name.value
name
@fetchDefault: (node) ->
try
node.value?.compile
indent: ''
catch error
if node?.value?.base?.value is 'this'
value = node.value.properties[0]?.name.compile
indent: ''
"@#{value}"
constructor: (@name, @splat, @default) ->
toString: ->
splat = '...' if @splat
defauld = " = #{@default}" if @default
[@name, splat, defauld].join('')
inspect: ->
{
name: @name
splat: @splat
default: @default
}
================================================
FILE: lib/tools/markdown.coffee
================================================
marked = require 'marked'
Tools = require '../_tools'
# It looks like all the markdown libraries for node doesn't get
# GitHub flavored markdown right. This helper class post-processes
# the best available output from the marked library to conform to
# GHM. In addition the allowed tags can be limited.
#
module.exports = class Tools.Markdown
# Tags to keep when parsing is limited
@limitedTags: 'a,abbr,acronym,b,big,cite,code,del,em,i,ins,sub,sup,span,small,strike,strong,q,tt,u'
# Convert markdown to Html. If the param `limit`
# is true, then all unwanted elements are stripped from the
# result and also all existing newlines.
#
# @param [String] markdown the markdown markup
# @param [Boolean] limit if elements should be limited
#
@convert: (markdown, limit = false, allowed = Markdown.limitedTags) ->
return if markdown is undefined
html = marked(markdown)
if limit
html = html.replace(/\n/, ' ')
html = Markdown.limit(html, allowed)
# Remove newlines around open and closing paragraph tags
html = html.replace /(?:\n+)?<(\/?p)>(?:\n+)?/mg, '<$1>'
# Add '.html' to relative markdown links
html = html.replace /href="(?!https?:\/\/)(.*\.md)"/mg, 'href="$1.html"'
html
# Strips all unwanted tag from the html
#
# @param [String] html the Html to clean
# @param [String] allowed the comma separated list of allowed tags
# @return [String] the cleaned Html
#
@limit: (html, allowed) ->
allowed = allowed.split ','
replace = (html) ->
result = html.replace /<([a-z0-9]+)\s*(?:\s[^>]+)?>([\s\S]+?)<\/\1>/g, (match, tag, text) ->
if allowed.indexOf(tag) is -1 then text else match
if result == html
result
else
replace(result)
replace(html)
================================================
FILE: lib/tools/referencer.coffee
================================================
Tools = require '../_tools'
module.exports = class Tools.Referencer
constructor: (@environment) ->
resolve: (text, replacer) ->
# Make curly braces within code blocks undetectable
text = text.replace /\`[^\`]*\`/mg, (match) -> match.replace(/\{/mg, "\u0091").replace(/\}/mg, "\u0092")
# Search for references and replace them
text = text.replace /\{([^\}]*)\}/gm, (match, link) =>
link = link.split(' ')
href = link.shift()
label = link.join(' ')
replacement = @environment.reference(href)
if replacement != href || /\:\/\/\w+((\:\d+)?\/\S*)?/.test(href)
replacer replacement, label || href
else
match
# Restore curly braces within code blocks
text = text.replace /\`[^\`]*\`/mg, (match) -> match.replace(/\u0091/mg, '{').replace(/\u0092/mg, '}')
================================================
FILE: lib/traverser.coffee
================================================
FS = require 'fs'
_ = require 'underscore'
_.str = require 'underscore.string'
CoffeeScript = require 'coffee-script'
Environment = require './environment'
Documentation = require './documentation'
File = require './entities/file'
Winston = require 'winston'
#
# The class takes CS nodes tree and recursively injects
# additional meta-data into it:
#
# 1. For each possible node it tries every registered
# entity and pushes an instance of it into tree if it suites.
# 2. For every suitable node it finds the suitable comment block
# respecting things like `this.` and `module.exports =` and
# links it to the tree as well.
#
# Since the transformation is happening upside down, nested entities
# can interact with initialized parents (for instance a class can find
# parent class; method can find the class/mixin/file it belongs to).
#
module.exports = class Traverser
@read: (file, environment) ->
content = FS.readFileSync(file, 'utf8')
content = @convertComments(content, environment.options.closure) unless environment.options.cautios
new @(file, content, environment)
# Attach each parent to its children, so we are able
# to traverse the ancestor parse tree. Since the
# parent attribute is already used in the class node,
# the parent is stored as `ancestor`.
#
# @param [Base] nodes the CoffeeScript nodes
#
@linkAncestors: (node) ->
node.eachChild (child) =>
child.ancestor = node
@linkAncestors child
node
# Convert the comments to block comments,
# so they appear in the nodes.
#
# The methods replaces starting # symbols with invisible
# unicode whitespace to keep empty lines formatted.
#
# @param [String] content the CoffeeScript file content
#
@convertComments: (content, closure=false) ->
result = []
comment = []
inComment = false
inBlockComment = false
indentComment = 0
for line in content.split('\n')
blockComment = /^\s*#{3}/.exec(line) && !/^\s*#{3}.+#{3}/.exec(line)
if blockComment || inBlockComment
line = line.replace /#{3}\*/, "###" if closure
inBlockComment = !inBlockComment if blockComment
result.push line
else
commentLine = /^(\s*#)\s?(\s*.*)/.exec(line)
if commentLine
if inComment
comment.push @whitespace(indentComment) + commentLine[2]?.replace /#/g, "\u0091#"
else
inComment = true
indentComment = commentLine[1].length - 1
comment.push @whitespace(indentComment) + '###'
comment.push @whitespace(indentComment) + commentLine[2]?.replace /#/g, "\u0091#"
else
if inComment
inComment = false
comment.push @whitespace(indentComment) + '###'
# Push here comments only before certain lines
if ///
( # class Foo
class\s*@?[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*
| # variable =
^\s*[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff.]*\s+\=
| # method: ->
(?:[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*|["'].*["'])\s*:\s*(\(.*\)\s*)?[-=]>
| # @method = ->
@[A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*\s*=\s*(\(.*\)\s*)?[-=]>
| # CONSTANT
^\s*@[$A-Z_][A-Z_]*
| # property:
^\s*[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*:
| # @property 'foo'
@[A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*\s+['"].+['"]
)
///.exec line
result.push c for c in comment
comment = []
result.push line
result.join('\n')
# Whitespace helper function
#
# @param [Number] n the number of spaces
# @return [String] the space string
#
@whitespace: (n) ->
a = []
while a.length < n
a.push ' '
a.join ''
constructor: (@path, @content, @environment) ->
@environment ?= new Environment
@history = []
@root = @constructor.linkAncestors(CoffeeScript.nodes @content)
@file = @prepare(@root, @path, File)
@root.traverseChildren true, (node) =>
for Entity in @environment.needles when Entity.looksLike(node)
Winston.info "Adding entity " + Entity.name if @environment.options.debug
@prepare(node, @file, Entity)
@history.push node
prepare: (node, file, Entity) ->
node.entities ?= []
unless node.documentation?
# Find actual comment node
previous = @history[@history.length-1]
if @environment.options.debug
Winston.info "Type of previous is " + previous?.constructor.name
Winston.info "History is " + @history.map (entry) -> entry.constructor.name
switch previous?.constructor.name
# A comment is preceding the entity declaration
when 'Comment'
doc = previous
when 'Literal', 'PropertyName'
# The node is exported `module.exports = ...`, take the comment before `module`
if previous.value is 'exports'
previous = @history[@history.length-6]
doc = previous if previous?.constructor.name is 'Comment'
# An assign that is handled as an object by CoffeeScript
when 'Obj'
if @history[@history.length-2]?.constructor.name is 'Value'
previous = @history[@history.length-3]
doc = previous if previous?.constructor.name is 'Comment'
# An operator precedes the definition, e.g. `new class ClassName`
when 'Op'
previous = @history[@history.length-2]
doc = previous if previous?.constructor.name is 'Comment'
Winston.info "Doc is " + doc?.comment if @environment.options.debug
if doc?.comment?
node.documentation = new Documentation(@leftTrimBlock doc.comment)
if Entity.is(node)
entity = new Entity @environment, file, node
node.entities.push(entity)
@environment.registerEntity(entity)
entity
# Detect whitespace on the left and removes
# the minimum whitespace ammount.
#
# The method additionally drops invisible UTF
# whitespace introduced by `convertComments`
#
# @example left trim all lines
# leftTrimBlock(['', ' Escape at maximum speed.', '', ' @param (see #move)', ' '])
# => ['', 'Escape at maximum speed.', '', '@param (see #move)', '']
#
# This will keep indention for examples intact.
#
# @param [Array] lines the comment lines
# @return [Array] lines left trimmed lines
#
leftTrimBlock: (text) ->
return unless text
lines = text.replace(/\u0091/gm, '').split('\n')
# Detect minimal left trim amount
trimMap = lines.map (line) ->
line.length - _.str.ltrim(line).length if line.length != 0
minimalTrim = _.min _.without(trimMap, undefined)
# If we have a common amount of left trim
if minimalTrim > 0 && minimalTrim < Infinity
# Trim same amount of left space on each line
lines = for line in lines
line = line.substring(minimalTrim, line.length)
line
# Strip empty prepending lines
lines = lines.slice(1) while lines[0].length == 0
# Strip empty postponing lines
lines = lines.slice(0, -1) while lines[lines.length-1].length == 0
lines
inspect: ->
@environment.inspect()
================================================
FILE: package.json
================================================
{
"name": "codo",
"description": "A CoffeeScript documentation generator.",
"keywords": [
"coffeescript",
"doc"
],
"author": "Michael Kessler ",
"maintainers": [
{
"name": "Michael Kessler"
},
{
"name": "Boris Staal"
}
],
"version": "2.1.2",
"license": "MIT",
"engines": {
"node": ">=0.9.0"
},
"directories": {
"lib": "./src",
"theme/default/assets": "./theme/default/assets",
"theme/default/templates": "./theme/default/templates"
},
"main": "./lib/codo.coffee",
"bin": {
"codo": "./bin/codo"
},
"dependencies": {
"coffee-script": ">= 1.6.0",
"walkdir": ">= 0.0.2",
"optimist": ">= 0.3.0",
"marked": ">= 0.2.10",
"underscore": ">= 0.1.0",
"underscore.string": ">= 0.1.0",
"haml-coffee": ">= 0.6.0",
"mkdirp": ">= 0.1.0",
"connect": ">= 0.1.0",
"colors": ">= 1.1.2",
"async": ">= 0.1.22",
"mincer": "~0.5.11",
"stylus": "~0.38.0",
"nib": "~1.0.0",
"winston": "~0.8.0",
"cli-table": "~0.2.0",
"strftime": "~0.6.2"
},
"devDependencies": {
"diff": ">=2.1.1",
"jasmine-node": ">= 1.13.1",
"deep-eql": "*",
"rimraf": "~2.2.2",
"grunt": "~0.4.1",
"grunt-release": "~0.6.0"
},
"homepage": "https://github.com/coffeedoc/codo",
"repository": {
"type": "git",
"url": "git://github.com/coffeedoc/codo.git"
},
"bugs": {
"url": "https://github.com/coffeedoc/codo/issues"
},
"scripts": {
"test": "jasmine-node --coffee spec"
}
}
================================================
FILE: spec/_templates/angular/angular-with-new.coffee
================================================
module.exports = require('angular').module 'module-name', ['some-dependency']
.factory 'a-factory', ['some-dependency', (someDependency) ->
###
This is a testy test class with a documenty documentation
###
new class MyTestClass
]
.name
================================================
FILE: spec/_templates/angular/angular-with-new.json
================================================
[
{
"file": "spec/_templates/angular/angular-with-new.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/angular/angular-with-new.coffee",
"documentation": {
"comment": "This is a testy test class with a documenty documentation",
"summary": "This is a testy test class with a documenty documentation"
},
"name": "MyTestClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/angular/angular.coffee
================================================
module.exports = require('angular').module 'module-name', ['some-dependency']
.factory 'a-factory', ['some-dependency', (someDependency) ->
###
This is a testy test class with a documenty documentation
###
class MyTestClass
]
.name
================================================
FILE: spec/_templates/angular/angular.json
================================================
[
{
"file": "spec/_templates/angular/angular.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/angular/angular.coffee",
"documentation": {
"comment": "This is a testy test class with a documenty documentation",
"summary": "This is a testy test class with a documenty documentation"
},
"name": "MyTestClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/class_description_markdown.coffee
================================================
# Codo - the CoffeeScript API documentation generator
#
# # Header 1
#
# This is a paragraph.
#
# ## Header 2
#
# This is a paragraph.
#
# ### Header 3
#
# This is a paragraph.
#
# #### Header 4
#
# This is a paragraph.
#
# ##### Header 5
#
# This is a paragraph.
#
# ###### Header 6
#
# This is a paragraph.
#
# @abstract _Template methods_ must be implemented
# @note Also notes have _now_ Markdown
# @todo Allow **markdown** in todos
#
# @author Mickey
# @author **Donald**
# @copyright _No Copyright_
# @since **1.0.0**
# @version _1.1.0_
# @deprecated **nobody** uses this
#
class TestMarkdownDocumentation
================================================
FILE: spec/_templates/classes/class_description_markdown.json
================================================
[
{
"file": "spec/_templates/classes/class_description_markdown.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/class_description_markdown.coffee",
"documentation": {
"comment": "Codo - the CoffeeScript API documentation generator\n\n# Header 1\n\nThis is a paragraph.\n\n## Header 2\n\nThis is a paragraph.\n\n### Header 3\n\nThis is a paragraph.\n\n#### Header 4\n\nThis is a paragraph.\n\n##### Header 5\n\nThis is a paragraph.\n\n###### Header 6\n\nThis is a paragraph.",
"summary": "Codo - the CoffeeScript API documentation generator",
"notes": [
"Also notes have _now_ Markdown"
],
"abstract": "_Template methods_ must be implemented",
"deprecated": "**nobody** uses this",
"version": "_1.1.0_",
"since": "**1.0.0**",
"authors": [
"Mickey",
"**Donald**"
],
"copyright": "_No Copyright_",
"todos": [
"Allow **markdown** in todos"
]
},
"name": "TestMarkdownDocumentation",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/class_documentation.coffee
================================================
# This is a test class with `inline.dot`. Beware.
#
# @note Please use
# this carefully
# @note For internal use only
#
# @example
# Class.getType('cat')
#
# @example Use it in this way
# new Class()
#
# @example Alternatively also this is possible
# cl = Class.for(obj)
# cl.execute()
#
# @todo Clean up socket handler
# @todo Refactor
# property factory
# @author Netzpirat
# @author Plasticman
# @abstract Each listener implementation must inherit
# @private
# @public
# @deprecated Use other class
# which is thread safe
# @since 1.0.0
# @version 1.0.2
#
class TestClassDocumentation
================================================
FILE: spec/_templates/classes/class_documentation.json
================================================
[
{
"file": "spec/_templates/classes/class_documentation.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/class_documentation.coffee",
"documentation": {
"comment": "This is a test class with `inline.dot`. Beware.",
"summary": "This is a test class with `inline.dot`.",
"notes": [
"Please use this carefully",
"For internal use only"
],
"abstract": "Each listener implementation must inherit",
"private": true,
"public": true,
"deprecated": "Use other class which is thread safe",
"version": "1.0.2",
"since": "1.0.0",
"authors": [
"Netzpirat",
"Plasticman"
],
"todos": [
"Clean up socket handler",
"Refactor property factory"
],
"examples": [
{
"title": "",
"code": "Class.getType('cat')"
},
{
"title": "Use it in this way",
"code": "new Class()"
},
{
"title": "Alternatively also this is possible",
"code": "cl = Class.for(obj)\ncl.execute()"
}
]
},
"name": "TestClassDocumentation",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/class_extends.coffee
================================================
class NS.Clazz extends Another.Clazz
================================================
FILE: spec/_templates/classes/class_extends.json
================================================
[
{
"file": "spec/_templates/classes/class_extends.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/class_extends.coffee",
"name": "NS.Clazz",
"parent": "Another.Clazz",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/empty_class.coffee
================================================
class A
class extends A
================================================
FILE: spec/_templates/classes/empty_class.json
================================================
[
{
"file": "spec/_templates/classes/empty_class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/empty_class.coffee",
"name": "A",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/export_class.coffee
================================================
# Yep, this should be assigned to the class
module.exports = class TestExportClass
================================================
FILE: spec/_templates/classes/export_class.json
================================================
[
{
"file": "spec/_templates/classes/export_class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/export_class.coffee",
"documentation": {
"comment": "Yep, this should be assigned to the class",
"summary": "Yep, this should be assigned to the class"
},
"name": "TestExportClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/global_class.coffee
================================================
# This is a test class
class @GlobalTestClass
================================================
FILE: spec/_templates/classes/global_class.json
================================================
[
{
"file": "spec/_templates/classes/global_class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/global_class.coffee",
"documentation": {
"comment": "This is a test class",
"summary": "This is a test class"
},
"selfish": true,
"name": "GlobalTestClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/inner_class.coffee
================================================
class Tower.Model.Relation.HasMany extends Tower.Model.Relation
class @Scope
class @Scope2 extends @Scope
================================================
FILE: spec/_templates/classes/inner_class.json
================================================
[
{
"file": "spec/_templates/classes/inner_class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/inner_class.coffee",
"name": "Tower.Model.Relation.HasMany",
"parent": "Tower.Model.Relation",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/classes/inner_class.coffee",
"selfish": true,
"name": "Tower.Model.Relation.HasMany.Scope",
"container": {
"file": "spec/_templates/classes/inner_class.coffee",
"name": "Tower.Model.Relation.HasMany",
"parent": "Tower.Model.Relation",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/classes/inner_class.coffee",
"selfish": true,
"name": "Tower.Model.Relation.HasMany.Scope2",
"container": {
"file": "spec/_templates/classes/inner_class.coffee",
"name": "Tower.Model.Relation.HasMany",
"parent": "Tower.Model.Relation",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
"parent": {
"file": "spec/_templates/classes/inner_class.coffee",
"selfish": true,
"name": "Tower.Model.Relation.HasMany.Scope",
"container": {
"file": "spec/_templates/classes/inner_class.coffee",
"name": "Tower.Model.Relation.HasMany",
"parent": "Tower.Model.Relation",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/namespaced_class.coffee
================================================
# Test description
class Some.Namespace.MyClass
# Test description
class @Another.Namespace.MyClass
# Test description
# @namespace Manual.Namespace
class MyClass
================================================
FILE: spec/_templates/classes/namespaced_class.json
================================================
[
{
"file": "spec/_templates/classes/namespaced_class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/namespaced_class.coffee",
"documentation": {
"comment": "Test description",
"summary": "Test description"
},
"name": "Some.Namespace.MyClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/classes/namespaced_class.coffee",
"documentation": {
"comment": "Test description",
"summary": "Test description"
},
"selfish": true,
"name": "Another.Namespace.MyClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/classes/namespaced_class.coffee",
"documentation": {
"comment": "Test description",
"summary": "Test description",
"namespace": "Manual.Namespace"
},
"name": "MyClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/classes/simple_class.coffee
================================================
class MyTestClass
================================================
FILE: spec/_templates/classes/simple_class.json
================================================
[
{
"file": "spec/_templates/classes/simple_class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/classes/simple_class.coffee",
"name": "MyTestClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/complicateds/methods.coffee
================================================
# @include Mixin
# @extend Mixin
# @concern Concern
#
# @method #x(key, value)
# Sets a value
#
class Class
z: ->
# @mixin
Mixin =
m: ->
# @mixin
Concern =
ClassMethods:
cs: ->
InstanceMethods:
cd: ->
class Subclass extends Class
x: ->
class Subsubclass extends Subclass
y: ->
================================================
FILE: spec/_templates/complicateds/variables.coffee
================================================
class Class
z: '123'
class Subclass extends Class
z: '456'
class Subsubclass extends Subclass
y: '123'
================================================
FILE: spec/_templates/environment/class.coffee
================================================
#
# @include LookAndFeel
#
class Fluffy
================================================
FILE: spec/_templates/environment/mixin.coffee
================================================
#
# @mixin
#
LookAndFeel =
look: ->
feel: ->
================================================
FILE: spec/_templates/environment/result.json
================================================
[
{
"file": "spec/_templates/environment/class.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/environment/class.coffee",
"documentation": {
"comment": "",
"summary": "",
"includes": [
"LookAndFeel"
]
},
"name": "Fluffy",
"methods": [],
"variables": [],
"properties": [],
"includes": [
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "LookAndFeel",
"documentation": {
"comment": "",
"summary": ""
},
"methods": [
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "look",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "feel",
"bound": false,
"parameters": []
}
],
"variables": []
}
],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/environment/mixin.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "LookAndFeel",
"documentation": {
"comment": "",
"summary": ""
},
"methods": [
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "look",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "feel",
"bound": false,
"parameters": []
}
],
"variables": []
},
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "look",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/environment/mixin.coffee",
"name": "feel",
"bound": false,
"parameters": []
}
]
================================================
FILE: spec/_templates/example/CHANGELOG
================================================
Changelog
This project is actually so incredible that it never changes.
Seriously!!!
================================================
FILE: spec/_templates/example/README.md
================================================
# Readme
That's how we do it, this is how we do it (c)
================================================
FILE: spec/_templates/example/package.json
================================================
{
"name": "example",
"description": "The best ever project about lines and wildness!",
"keywords": [
"coffeescript",
"doc"
],
"version": "0.1.0"
}
================================================
FILE: spec/_templates/example/src/angry_animal.coffee
================================================
# Ola-la
# @abstract
TROLOLO = 'test'
# @mixin
Example.AngryAnimal =
roar: ->
# Ola-la once more
#
# @overload ?globalMethod(a, b)
# Overload this animal for once!
#
# @deprecated Don't use it anymore!
globalMethod = ({a, b}) ->
================================================
FILE: spec/_templates/example/src/animal.coffee
================================================
# Base class for all animals.
#
# @note This is not used for codo, its purpose is to show
# all possible tags within a class.
#
# @todo Provide more examples
#
# @example How to subclass an animal
# class Lion extends Animal
# move: (direction, speed): ->
#
# @abstract Each animal implementation must inherit from {Animal}
#
# @author Michael Kessler
# @deprecated This class is not used anymore
# @version 0.2.0
# @since 0.1.0
#
class Example.Animal
# The Answer to the Ultimate Question of Life, the Universe, and Everything
@ANSWER = 42
# Construct a new animal.
#
# @todo Clean up
# @param [String] name the name of the animal
# @param [Date] birthDate when the animal was born
#
constructor: (@name, @birthDate = new Date()) ->
# Move the animal.
#
# @example Move an animal
# new Lion('Simba').move('south', 12)
#
# @abstract
# @param [Object] options the moving options
# @option options [String] direction the moving direction
# @option options [Number] speed the speed in mph
#
move: (options = {}) ->
# Copulate another animal.
#
# @note Don't take it seriously
#
# @private
# @author Michael Kessler
# @param [Animal] animal the partner animal
# @return [Boolean] true when success
# @deprecated Do not copulate
# @version 0.2.0
# @since 0.1.0
#
copulate: (animal) =>
# Moves all animal into the ark.
#
# @return [Boolean] true when all in Ark
#
@enterArk: ->
================================================
FILE: spec/_templates/example/src/lion.coffee
================================================
# A dangerous lion. Take care.
#
# @author Simba
# @see http://en.wikipedia.org/wiki/Lion
# @include Example.AngryAnimal
# @extend MissingMixin
#
class Example.Animal.Lion extends Example.Animal
# Maximum speed in MPH
@MAX_SPEED = 50
# Move the lion fast
#
# @param [String] direction the moving direction
# @param [Number] speed the moving speed
#
move: (direction, speed) ->
super({ diection: direction, speed: speed })
# Escape at maximum speed.
#
# @param (see #move)
#
escape: (direction) ->
@move(direction, @MAX_SPEED)
================================================
FILE: spec/_templates/example/src/over_documented_class.coffee
================================================
#
# This class is SO DOCUMENTED! Seriously, Just look at that! This is so incredible!
#
# It even has some links: {http://www.google.com Google for instance} {OverDocumentedClass}
# {OverDocumentedClass itself} {http://www.github.com} {OverDocumentedMixin~mixed_method}
# {Casper The ghost!}
#
# @abstract It's so abstract! ^_^ {http://www.github.com}
# @author The great Yoda {http://www.github.com}
# @include OverDocumentedMixin
# @include Casper
# @extend OverDocumentedMixin
# @extend Casper
# @copyright The great Yoda {http://www.github.com}
# @deprecated Don't use this anymore!!11
# @example Foobar
# foo = bar
# @note Never fortget this thing! {http://www.github.com}
# @method #virtual_method({a, b})
# This is the virtual method ZOMG
# @private
# @see www.github.com
# @since 1.0
# @todo Run with the wolves {http://www.github.com}
# @version 1.1
class OverDocumentedClass
# The constant that is SO DOCUMENTED as well (I feel sick about it)
# @abstract It's so abstract! ^_^ {http://www.github.com}
# @author The great Yoda {http://www.github.com}
# @copyright The great Yoda {http://www.github.com}
# @deprecated Don't use this anymore!!11
# @example Foobar
# foo = bar
# @note Never fortget this thing! {http://www.github.com}
# @private
# @see www.github.com
# @since 1.0
# @todo Run with the wolves {http://www.github.com}
# @version 1.1
CONSTANT:
foo: 'bar'
# @property [OverDocumentedClass] Returns the {OverDocumentedClass clone}.
@property 'clone',
get: -> new OverDocumentedClass
# @abstract It's so abstract! ^_^ {http://www.github.com}
# @author The great Yoda {http://www.github.com}
# @copyright The great Yoda {http://www.github.com}
# @deprecated Don't use this anymore!!11
# @example Foobar
# foo = bar
# @note Never fortget this thing! {http://www.github.com}
# @private
# @see www.github.com
# @since 1.0
# @todo Run with the wolves {http://www.github.com}
# @version 1.1
# @throw [OverDocumentedClass] EXCEPTION OMG
# @return [OverDocumentedClass] RETVAL OMG
# @param [String] foo The first parameter
# @param [Integer] bar The second parameter (all of a sudden)
# @option options [String] option The only option (wtf?)
# @event simpleEvent
# @event notSoSimpleEvent Having description
# @event complicatedEvent
# Having description and parameters
# @param [String] The string
@class_method: (foo, bar, options={}) ->
# @abstract It's so abstract! ^_^ {http://www.github.com}
# @author The great Yoda {http://www.github.com}
# @copyright The great Yoda {http://www.github.com}
# @deprecated Don't use this anymore!!11
# @example Foobar
# foo = bar
# @note Never fortget this thing! {http://www.github.com}
# @private
# @see www.github.com
# @since 1.0
# @todo Run with the wolves {http://www.github.com}
# @version 1.1
# @throw [String] EXCEPTION OMG
# @return [String] RETVAL OMG
# @overload #instance_method(foo, bar)
# Obviously you can omit the last parameter
# @overload #instance_method(foo, bar, options)
# Or you can set it!
instance_method: (foo, bar, options={}) ->
================================================
FILE: spec/_templates/example/src/over_documented_mixin.coffee
================================================
#
# This mixin is SO DOCUMENTED! Seriously, Just look at that! This is so incredible!
#
# It even has some links: {http://www.google.com Google for instance} {OverDocumentedMixin}
# {OverDocumentedMixin itself} {http://www.github.com} {OverDocumentedClass.class_method}
# {Casper The ghost!}
#
# @mixin
# @abstract It's so abstract! ^_^ {http://www.github.com}
# @author The great Yoda {http://www.github.com}
# @copyright The great Yoda {http://www.github.com}
# @deprecated Don't use this anymore!!11
# @example Foobar
# foo = bar
# @note Never fortget this thing! {http://www.github.com}
# @method #virtual_method({a, b})
# This is the virtual method ZOMG
# @private
# @see www.github.com
# @since 1.0
# @todo Run with the wolves {http://www.github.com}
# @version 1.1
OverDocumentedMixin =
mixed_method: ->
================================================
FILE: spec/_templates/extras/README
================================================
This is a test README
================================================
FILE: spec/_templates/extras/README.md
================================================
# This is a test README
We even have some content here. [With links!](http://github.com)
## And nested menus
And even more content
### Actually...
I feel terribly sick writing this. It's like talking to myself.
[With relative markdown links too](SomeOtherFile.md)
================================================
FILE: spec/_templates/files/non_class_file.coffee
================================================
# The greeting
GREETING = 'Hay'
# Hey, check this out!
module.exports = FOO = 'Foo constant!'
# Says hello to a person
#
# @param [String] name the name of the person
#
hello = (name) ->
console.log GREETING, name
# Says bye to a person
#
# @param [String] name the name of the person
#
bye = (name) ->
console.log "Bye, bye #{ name }"
# Say hi to a person
#
# @param [String] name the name of the person
#
module.exports.sayHi = (hi) -> console.log "Hi #{ hi}!"
# A fooer for fooing foos.
#
# @note Foo
module.exports = foo = (foos) ->
logger.info 'Fooing...'
================================================
FILE: spec/_templates/files/non_class_file.json
================================================
[
{
"file": "spec/_templates/files/non_class_file.coffee",
"methods": [
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "hello",
"bound": false,
"documentation": {
"comment": "Says hello to a person",
"summary": "Says hello to a person",
"params": [
{
"type": "String",
"name": "name",
"description": "the name of the person"
}
]
},
"parameters": [
{
"name": "name",
"splat": false
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "bye",
"bound": false,
"documentation": {
"comment": "Says bye to a person",
"summary": "Says bye to a person",
"params": [
{
"type": "String",
"name": "name",
"description": "the name of the person"
}
]
},
"parameters": [
{
"name": "name",
"splat": false
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "sayHi",
"bound": false,
"documentation": {
"comment": "Say hi to a person",
"summary": "Say hi to a person",
"params": [
{
"type": "String",
"name": "name",
"description": "the name of the person"
}
]
},
"parameters": [
{
"name": "hi",
"splat": false
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "foo",
"bound": false,
"documentation": {
"comment": "A fooer for fooing foos.",
"summary": "A fooer for fooing foos.",
"notes": [
"Foo"
]
},
"parameters": [
{
"name": "foos",
"splat": false
}
]
}
],
"variables": [
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "GREETING",
"constant": true,
"value": "'Hay'",
"documentation": {
"comment": "The greeting",
"summary": "The greeting"
}
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "FOO",
"constant": true,
"value": "'Foo constant!'",
"documentation": {
"comment": "Hey, check this out!",
"summary": "Hey, check this out!"
}
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "GREETING",
"constant": true,
"value": "'Hay'",
"documentation": {
"comment": "The greeting",
"summary": "The greeting"
}
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "FOO",
"constant": true,
"value": "'Foo constant!'",
"documentation": {
"comment": "Hey, check this out!",
"summary": "Hey, check this out!"
}
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "hello",
"bound": false,
"documentation": {
"comment": "Says hello to a person",
"summary": "Says hello to a person",
"params": [
{
"type": "String",
"name": "name",
"description": "the name of the person"
}
]
},
"parameters": [
{
"name": "name",
"splat": false
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "bye",
"bound": false,
"documentation": {
"comment": "Says bye to a person",
"summary": "Says bye to a person",
"params": [
{
"type": "String",
"name": "name",
"description": "the name of the person"
}
]
},
"parameters": [
{
"name": "name",
"splat": false
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "sayHi",
"bound": false,
"documentation": {
"comment": "Say hi to a person",
"summary": "Say hi to a person",
"params": [
{
"type": "String",
"name": "name",
"description": "the name of the person"
}
]
},
"parameters": [
{
"name": "hi",
"splat": false
}
]
},
{
"file": "spec/_templates/files/non_class_file.coffee",
"name": "foo",
"bound": false,
"documentation": {
"comment": "A fooer for fooing foos.",
"summary": "A fooer for fooing foos.",
"notes": [
"Foo"
]
},
"parameters": [
{
"name": "foos",
"splat": false
}
]
}
]
================================================
FILE: spec/_templates/methods/assigned_parameters.coffee
================================================
class TestAssignedParameters
help: (@me) ->
================================================
FILE: spec/_templates/methods/assigned_parameters.json
================================================
[
{
"file": "spec/_templates/methods/assigned_parameters.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/assigned_parameters.coffee",
"name": "TestAssignedParameters",
"methods": [
{
"file": "spec/_templates/methods/assigned_parameters.coffee",
"name": "help",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "me",
"splat": false
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/assigned_parameters.coffee",
"name": "help",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "me",
"splat": false
}
]
}
]
================================================
FILE: spec/_templates/methods/class_methods.coffee
================================================
class TestClassMethods
@helper: ->
@another: (a, b) ->
@withDefault: (a = 2, c, d = 'hi', d, e = { a: 2 }, f = new TestClassMethods()) ->
@nowWithSpalt: (foo, bar...) ->
@andSoDoesThis = (foo, bar) ->
================================================
FILE: spec/_templates/methods/class_methods.json
================================================
[
{
"file": "spec/_templates/methods/class_methods.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "TestClassMethods",
"methods": [
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "helper",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": []
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "another",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "withDefault",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "a",
"splat": false,
"default": "2"
},
{
"name": "c",
"splat": false
},
{
"name": "d",
"splat": false,
"default": "'hi'"
},
{
"name": "d",
"splat": false
},
{
"name": "e",
"splat": false,
"default": "{\n a: 2\n}"
},
{
"name": "f",
"splat": false,
"default": "new TestClassMethods()"
}
]
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "nowWithSpalt",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": true
}
]
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "andSoDoesThis",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": false
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "helper",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": []
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "another",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "withDefault",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "a",
"splat": false,
"default": "2"
},
{
"name": "c",
"splat": false
},
{
"name": "d",
"splat": false,
"default": "'hi'"
},
{
"name": "d",
"splat": false
},
{
"name": "e",
"splat": false,
"default": "{\n a: 2\n}"
},
{
"name": "f",
"splat": false,
"default": "new TestClassMethods()"
}
]
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "a",
"constant": false,
"value": "2"
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "nowWithSpalt",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": true
}
]
},
{
"file": "spec/_templates/methods/class_methods.coffee",
"name": "andSoDoesThis",
"bound": false,
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": false
}
]
}
]
================================================
FILE: spec/_templates/methods/curly_method_documentation.coffee
================================================
class App.TestMethodDocumentation extends App.Doc
# Should be overloaded to change fetch limit.
#
# @return Number of items per fetch
#
fetchLimit: () -> 5
# Do it!
#
# @see #undo for more information
#
# @private
# @param {String} it The thing to do
# @param again {Boolean} Do it again
# @param {Object} options The do options
# @option options {String} speed The speed
# @option options {Number} repeat How wany time to repeat
# @option options {Array} tasks The tasks to do
# @return {Boolean} When successful executed
#
do: (it, again, options) ->
# Do it!
#
# @see #undo for more information
#
# @private
# @param {String} it The thing to do
# @param again {Boolean} Do it again
# @param {Object} options The do options
# @option options {String} speed The speed
# @option options {Number} repeat How wany time to repeat
# @option options {Array} tasks The tasks to do
# @return {Boolean} When successful executed
#
doWithoutSpace:(it, again, options)->
# Do it!
#
# @see {#undo} for more information
#
# @private
# @param {String} it The thing to do
# @param {Object} options The do options
# @option options {String} speed The speed
# @option options {Number} repeat How wany time to repeat
# @option options {Array} tasks The tasks to do
# @return {Boolean} When successful executed
#
@lets_do_it = (it, options) ->
================================================
FILE: spec/_templates/methods/curly_method_documentation.json
================================================
[
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "App.TestMethodDocumentation",
"parent": "App.Doc",
"methods": [
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "fetchLimit",
"bound": false,
"documentation": {
"comment": "Should be overloaded to change fetch limit.",
"summary": "Should be overloaded to change fetch limit.",
"returns": {
"type": "?",
"description": "Number of items per fetch"
}
},
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "do",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "doWithoutSpace",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "lets_do_it",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "{#undo}",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "options",
"splat": false
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "fetchLimit",
"bound": false,
"documentation": {
"comment": "Should be overloaded to change fetch limit.",
"summary": "Should be overloaded to change fetch limit.",
"returns": {
"type": "?",
"description": "Number of items per fetch"
}
},
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "do",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "doWithoutSpace",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/curly_method_documentation.coffee",
"name": "lets_do_it",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "{#undo}",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "options",
"splat": false
}
]
}
]
================================================
FILE: spec/_templates/methods/dynamic_methods.coffee
================================================
# This class has virtual methods, which doesn't
# exist in the source but appears in the documentation.
#
# @method #set(key, value)
# Sets a value
# @param [String] key describe key param
# @param [Object] value describe value param
# @option value [String] string
# @option value [Integer] number
# @option value [Object] whatever
#
# @method .get(key)
# Gets a value
# @param [String] key describe key param
# @return [Object] describe value param
#
# @method #delete({key, passion}, foo='bar')
# Deletes a key from the data.
#
# Another line
#
# @param [String] key describe key param
#
# @example Delete a key.
# emv = new Example.Methods.Virtual
# emv.set 'foo', 'bar'
# val = emv.get 'foo'
#
# # now, proclaim you're done with foo.
# emv.delete 'foo'
#
# This line should be part of the class description, and the method declaration
# shouldn't have messed it up.
#
class Example.Methods.Virtual
================================================
FILE: spec/_templates/methods/dynamic_methods.json
================================================
[
{
"file": "spec/_templates/methods/dynamic_methods.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/dynamic_methods.coffee",
"documentation": {
"comment": "This class has virtual methods, which doesn't\nexist in the source but appears in the documentation.\n\nThis line should be part of the class description, and the method declaration\nshouldn't have messed it up.",
"summary": "This class has virtual methods, which doesn't\nexist in the source but appears in the documentation.",
"methods": [
{
"signature": "#set(key, value)",
"documentation": {
"params": [
{
"type": "String",
"name": "key",
"description": "describe key param"
},
{
"type": "Object",
"name": "value",
"description": "describe value param"
}
],
"options": {
"value": [
{
"type": "String",
"name": "string"
},
{
"type": "Integer",
"name": "number"
},
{
"type": "Object",
"name": "whatever"
}
]
},
"comment": "Sets a value",
"summary": "Sets a value"
}
},
{
"signature": ".get(key)",
"documentation": {
"params": [
{
"type": "String",
"name": "key",
"description": "describe key param"
}
],
"returns": {
"type": "Object",
"description": "describe value param"
},
"comment": "Gets a value",
"summary": "Gets a value"
}
},
{
"signature": "#delete({key, passion}, foo='bar')",
"documentation": {
"params": [
{
"type": "String",
"name": "key",
"description": "describe key param"
}
],
"examples": [
{
"title": "Delete a key.",
"code": "emv = new Example.Methods.Virtual\nemv.set 'foo', 'bar'\nval = emv.get 'foo'\n\n# now, proclaim you're done with foo.\nemv.delete 'foo'"
}
],
"comment": "Deletes a key from the data.\n\nAnother line",
"summary": "Deletes a key from the data."
}
}
]
},
"name": "Example.Methods.Virtual",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/methods/instance_methods.coffee
================================================
class TestInstanceMethods
helper: ->
another: (param, obj) ->
anotherWithValues: (param = 123, obj = { a: 1 }, yup, comp = new TestInstanceMethods()) ->
nowWithSpalt: (foo, bar...) ->
bound: =>
# This is not exposed to the outside world.
internalToClassClosure = -> alert 'internal!'
================================================
FILE: spec/_templates/methods/instance_methods.json
================================================
[
{
"file": "spec/_templates/methods/instance_methods.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "TestInstanceMethods",
"methods": [
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "helper",
"bound": false,
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "another",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "param",
"splat": false
},
{
"name": "obj",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "anotherWithValues",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "param",
"splat": false,
"default": "123"
},
{
"name": "obj",
"splat": false,
"default": "{\n a: 1\n}"
},
{
"name": "yup",
"splat": false
},
{
"name": "comp",
"splat": false,
"default": "new TestInstanceMethods()"
}
]
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "nowWithSpalt",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": true
}
]
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "bound",
"bound": true,
"kind": "dynamic",
"parameters": []
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "helper",
"bound": false,
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "another",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "param",
"splat": false
},
{
"name": "obj",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "anotherWithValues",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "param",
"splat": false,
"default": "123"
},
{
"name": "obj",
"splat": false,
"default": "{\n a: 1\n}"
},
{
"name": "yup",
"splat": false
},
{
"name": "comp",
"splat": false,
"default": "new TestInstanceMethods()"
}
]
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "a",
"constant": false,
"value": "1"
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "nowWithSpalt",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": true
}
]
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "bound",
"bound": true,
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/instance_methods.coffee",
"name": "internalToClassClosure",
"bound": false,
"documentation": {
"comment": "This is not exposed to the outside world.",
"summary": "This is not exposed to the outside world."
},
"parameters": []
}
]
================================================
FILE: spec/_templates/methods/method_documentation.coffee
================================================
class App.TestMethodDocumentation extends App.Doc
# Should be overloaded to change fetch limit.
#
# @public
# @return Number of items per fetch
#
fetchLimit: () -> 5
# Do it!
#
# @see #undo for more information
#
# @private
# @param [String] it The thing to do
# @param again [Boolean] Do it again
# @param [Object] options The do options
# @option options [String] speed The speed
# @option options [Number] repeat How wany time to repeat
# @option options [Array] tasks The tasks to do
# @return [Boolean] When successful executed
# @throw [TypeError] when it can't be done
#
do: (it, again, options) ->
# Do it!
#
# @see #undo for more information
#
# @private
# @param [String] it The thing to do
# @param again [Boolean] Do it again
# @param [Object] options The do options
# @option options [String] speed The speed
# @option options [Number] repeat How wany time to repeat
# @option options [Array] tasks The tasks to do
# @return [Boolean] When successful executed
#
doWithoutSpace:(it, again, options)->
# Do it!
#
# @see {#undo} for more information
#
# @private
# @param [String] it The thing to do
# @param [Object] options The do options
# @option options [String] speed The speed
# @option options [Number] repeat How wany time to repeat
# @option options [Array] tasks The tasks to do
# @return [Boolean] When successful executed
#
@lets_do_it = (it, options) ->
================================================
FILE: spec/_templates/methods/method_documentation.json
================================================
[
{
"file": "spec/_templates/methods/method_documentation.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "App.TestMethodDocumentation",
"parent": "App.Doc",
"methods": [
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "fetchLimit",
"bound": false,
"documentation": {
"comment": "Should be overloaded to change fetch limit.",
"summary": "Should be overloaded to change fetch limit.",
"public": true,
"returns": {
"type": "?",
"description": "Number of items per fetch"
}
},
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "do",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
},
"throws": [
{
"type": "TypeError",
"description": "when it can't be done"
}
]
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "doWithoutSpace",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "lets_do_it",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "{#undo}",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "options",
"splat": false
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "fetchLimit",
"bound": false,
"documentation": {
"comment": "Should be overloaded to change fetch limit.",
"summary": "Should be overloaded to change fetch limit.",
"public": true,
"returns": {
"type": "?",
"description": "Number of items per fetch"
}
},
"kind": "dynamic",
"parameters": []
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "do",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
},
"throws": [
{
"type": "TypeError",
"description": "when it can't be done"
}
]
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "doWithoutSpace",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "#undo",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Boolean",
"name": "again",
"description": "Do it again"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"kind": "dynamic",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "again",
"splat": false
},
{
"name": "options",
"splat": false
}
]
},
{
"file": "spec/_templates/methods/method_documentation.coffee",
"name": "lets_do_it",
"bound": false,
"documentation": {
"comment": "Do it!",
"summary": "Do it!",
"see": [
{
"reference": "{#undo}",
"label": "for more information"
}
],
"private": true,
"params": [
{
"type": "String",
"name": "it",
"description": "The thing to do"
},
{
"type": "Object",
"name": "options",
"description": "The do options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "speed",
"description": "The speed"
},
{
"type": "Number",
"name": "repeat",
"description": "How wany time to repeat"
},
{
"type": "Array",
"name": "tasks",
"description": "The tasks to do"
}
]
},
"returns": {
"type": "Boolean",
"description": "When successful executed"
}
},
"selfish": true,
"kind": "static",
"parameters": [
{
"name": "it",
"splat": false
},
{
"name": "options",
"splat": false
}
]
}
]
================================================
FILE: spec/_templates/methods/method_events.coffee
================================================
class Example.Events
# This is a generic Events emit method.
#
# @event theBasic.One
# @event theWeirdOne Omg this event is so weird o_O
# @event theComplicatedOne
# This is the complicated event yo
# @param [String] the incredible string
#
# This should be more description for the method itself, and events
# shouldn't have messed it up.
#
emit: (args...) ->
================================================
FILE: spec/_templates/methods/method_events.json
================================================
[
{
"file": "spec/_templates/methods/method_events.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/method_events.coffee",
"name": "Example.Events",
"methods": [
{
"file": "spec/_templates/methods/method_events.coffee",
"name": "emit",
"bound": false,
"documentation": {
"comment": "This is a generic Events emit method.\n\nThis should be more description for the method itself, and events\nshouldn't have messed it up.",
"summary": "This is a generic Events emit method.",
"events": [
{
"name": "theBasic.One",
"documentation": {
"comment": "",
"summary": ""
}
},
{
"name": "theWeirdOne",
"documentation": {
"comment": "Omg this event is so weird o_O",
"summary": "Omg this event is so weird o_O"
}
},
{
"name": "theComplicatedOne",
"documentation": {
"params": [
{
"type": "String",
"name": "the",
"description": "incredible string"
}
],
"comment": "This is the complicated event yo",
"summary": "This is the complicated event yo"
}
}
]
},
"kind": "dynamic",
"parameters": [
{
"name": "args",
"splat": true
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/method_events.coffee",
"name": "emit",
"bound": false,
"documentation": {
"comment": "This is a generic Events emit method.\n\nThis should be more description for the method itself, and events\nshouldn't have messed it up.",
"summary": "This is a generic Events emit method.",
"events": [
{
"name": "theBasic.One",
"documentation": {
"comment": "",
"summary": ""
}
},
{
"name": "theWeirdOne",
"documentation": {
"comment": "Omg this event is so weird o_O",
"summary": "Omg this event is so weird o_O"
}
},
{
"name": "theComplicatedOne",
"documentation": {
"params": [
{
"type": "String",
"name": "the",
"description": "incredible string"
}
],
"comment": "This is the complicated event yo",
"summary": "This is the complicated event yo"
}
}
]
},
"kind": "dynamic",
"parameters": [
{
"name": "args",
"splat": true
}
]
}
]
================================================
FILE: spec/_templates/methods/method_example.coffee
================================================
class TestExample
# @example Run generation
# codo = require 'codo'
#
# file = (filename, content) ->
# console.log "New file %s with content %s", filename, content
#
# done = (err) ->
# if err
# console.log "Cannot generate documentation:", err
# else
# console.log "Documentation generated"
#
# codo.run file, done
run: ->
================================================
FILE: spec/_templates/methods/method_example.json
================================================
[
{
"file": "spec/_templates/methods/method_example.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/method_example.coffee",
"name": "TestExample",
"methods": [
{
"file": "spec/_templates/methods/method_example.coffee",
"name": "run",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"examples": [
{
"title": "Run generation",
"code": "codo = require 'codo'\n\nfile = (filename, content) ->\n console.log \"New file %s with content %s\", filename, content\n\ndone = (err) ->\n if err\n console.log \"Cannot generate documentation:\", err\n else\n console.log \"Documentation generated\"\n\ncodo.run file, done"
}
]
},
"kind": "dynamic",
"parameters": []
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/method_example.coffee",
"name": "run",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"examples": [
{
"title": "Run generation",
"code": "codo = require 'codo'\n\nfile = (filename, content) ->\n console.log \"New file %s with content %s\", filename, content\n\ndone = (err) ->\n if err\n console.log \"Cannot generate documentation:\", err\n else\n console.log \"Documentation generated\"\n\ncodo.run file, done"
}
]
},
"kind": "dynamic",
"parameters": []
}
]
================================================
FILE: spec/_templates/methods/named_parameters.coffee
================================================
class TestNamedParameters
constructor : ({@cat, @dog, eel}, @fish, gorilla = 400, hog...) ->
burp : ({a, b, c}, e = 20, {f, g, h}, i = 10) ->
================================================
FILE: spec/_templates/methods/named_parameters.json
================================================
[
{
"file": "spec/_templates/methods/named_parameters.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/named_parameters.coffee",
"name": "TestNamedParameters",
"methods": [
{
"file": "spec/_templates/methods/named_parameters.coffee",
"name": "constructor",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "{cat, dog, eel}",
"splat": false
},
{
"name": "fish",
"splat": false
},
{
"name": "gorilla",
"splat": false,
"default": "400"
},
{
"name": "hog",
"splat": true
}
]
},
{
"file": "spec/_templates/methods/named_parameters.coffee",
"name": "burp",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "{a, b, c}",
"splat": false
},
{
"name": "e",
"splat": false,
"default": "20"
},
{
"name": "{f, g, h}",
"splat": false
},
{
"name": "i",
"splat": false,
"default": "10"
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/named_parameters.coffee",
"name": "constructor",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "{cat, dog, eel}",
"splat": false
},
{
"name": "fish",
"splat": false
},
{
"name": "gorilla",
"splat": false,
"default": "400"
},
{
"name": "hog",
"splat": true
}
]
},
{
"file": "spec/_templates/methods/named_parameters.coffee",
"name": "burp",
"bound": false,
"kind": "dynamic",
"parameters": [
{
"name": "{a, b, c}",
"splat": false
},
{
"name": "e",
"splat": false,
"default": "20"
},
{
"name": "{f, g, h}",
"splat": false
},
{
"name": "i",
"splat": false,
"default": "10"
}
]
}
]
================================================
FILE: spec/_templates/methods/overload_method.coffee
================================================
class Example.Overload
# This is a generic Store set method.
#
# @overload set(key, value)
# Sets a value on key
# @param [Symbol] key describe key param
# @param [Object] value describe value param
# @param [Object] options the options
# @option options test [String] the test option
#
# @overload set(value)
#
# Sets a value on the default key `:foo`
#
# @param [Object] value describe value param
# @return [Boolean] true when success
#
# This should be more description for the method itself, and overload
# shouldn't have messed it up.
#
set: (args...) ->
================================================
FILE: spec/_templates/methods/overload_method.json
================================================
[
{
"file": "spec/_templates/methods/overload_method.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/methods/overload_method.coffee",
"name": "Example.Overload",
"methods": [
{
"file": "spec/_templates/methods/overload_method.coffee",
"name": "set",
"bound": false,
"documentation": {
"comment": "This is a generic Store set method.\n\nThis should be more description for the method itself, and overload\nshouldn't have messed it up.",
"summary": "This is a generic Store set method.",
"overloads": [
{
"signature": "set(key, value)",
"documentation": {
"params": [
{
"type": "Symbol",
"name": "key",
"description": "describe key param"
},
{
"type": "Object",
"name": "value",
"description": "describe value param"
},
{
"type": "Object",
"name": "options",
"description": "the options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "test",
"description": "the test option"
}
]
},
"comment": "Sets a value on key",
"summary": "Sets a value on key"
}
},
{
"signature": "set(value)",
"documentation": {
"params": [
{
"type": "Object",
"name": "value",
"description": "describe value param"
}
],
"returns": {
"type": "Boolean",
"description": "true when success"
},
"comment": "Sets a value on the default key `:foo`",
"summary": "Sets a value on the default key `:foo`"
}
}
]
},
"kind": "dynamic",
"parameters": [
{
"name": "args",
"splat": true
}
]
}
],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/methods/overload_method.coffee",
"name": "set",
"bound": false,
"documentation": {
"comment": "This is a generic Store set method.\n\nThis should be more description for the method itself, and overload\nshouldn't have messed it up.",
"summary": "This is a generic Store set method.",
"overloads": [
{
"signature": "set(key, value)",
"documentation": {
"params": [
{
"type": "Symbol",
"name": "key",
"description": "describe key param"
},
{
"type": "Object",
"name": "value",
"description": "describe value param"
},
{
"type": "Object",
"name": "options",
"description": "the options"
}
],
"options": {
"options": [
{
"type": "String",
"name": "test",
"description": "the test option"
}
]
},
"comment": "Sets a value on key",
"summary": "Sets a value on key"
}
},
{
"signature": "set(value)",
"documentation": {
"params": [
{
"type": "Object",
"name": "value",
"description": "describe value param"
}
],
"returns": {
"type": "Boolean",
"description": "true when success"
},
"comment": "Sets a value on the default key `:foo`",
"summary": "Sets a value on the default key `:foo`"
}
}
]
},
"kind": "dynamic",
"parameters": [
{
"name": "args",
"splat": true
}
]
}
]
================================================
FILE: spec/_templates/mixins/concern.coffee
================================================
# This is my concern
# @mixin
Example.Mixins.Concern =
ClassMethods:
# @param [String] a This is the a parameter
# @param [String] b This is the a parameter
# @param [String] c This is the a parameter
a: (a, b, c) ->
# @param [String] x This is the x parameter
# @param [String] y This is the y parameter
# @param [String] z This is the z parameter
z: (x, y, z) ->
InstanceMethods:
# Say hi
# @param [String] to the name
hi: (to) ->
# Say goodbye
# @param [String] to the name
goodbye: (to) ->
# @concern Example.Mixins.Concern
class Example.Concern
================================================
FILE: spec/_templates/mixins/concern.json
================================================
[
{
"file": "spec/_templates/mixins/concern.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "Example.Mixins.Concern",
"concern": true,
"documentation": {
"comment": "This is my concern",
"summary": "This is my concern"
},
"methods": [],
"classMethods": [
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "a",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"params": [
{
"type": "String",
"name": "a",
"description": "This is the a parameter"
},
{
"type": "String",
"name": "b",
"description": "This is the a parameter"
},
{
"type": "String",
"name": "c",
"description": "This is the a parameter"
}
]
},
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
},
{
"name": "c",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "z",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"params": [
{
"type": "String",
"name": "x",
"description": "This is the x parameter"
},
{
"type": "String",
"name": "y",
"description": "This is the y parameter"
},
{
"type": "String",
"name": "z",
"description": "This is the z parameter"
}
]
},
"parameters": [
{
"name": "x",
"splat": false
},
{
"name": "y",
"splat": false
},
{
"name": "z",
"splat": false
}
]
}
],
"instanceMethods": [
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "hi",
"bound": false,
"documentation": {
"comment": "Say hi",
"summary": "Say hi",
"params": [
{
"type": "String",
"name": "to",
"description": "the name"
}
]
},
"parameters": [
{
"name": "to",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "goodbye",
"bound": false,
"documentation": {
"comment": "Say goodbye",
"summary": "Say goodbye",
"params": [
{
"type": "String",
"name": "to",
"description": "the name"
}
]
},
"parameters": [
{
"name": "to",
"splat": false
}
]
}
],
"variables": []
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "ClassMethods",
"constant": false,
"value": "{\n\n /*\n @param [String] a This is the a parameter\n @param [String] b This is the a parameter\n @param [String] c This is the a parameter\n */\n a: function(a, b, c) {},\n\n /*\n @param [String] x This is the x parameter\n @param [String] y This is the y parameter\n @param [String] z This is the z parameter\n */\n z: function(x, y, z) {}\n}"
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "a",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"params": [
{
"type": "String",
"name": "a",
"description": "This is the a parameter"
},
{
"type": "String",
"name": "b",
"description": "This is the a parameter"
},
{
"type": "String",
"name": "c",
"description": "This is the a parameter"
}
]
},
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
},
{
"name": "c",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "z",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"params": [
{
"type": "String",
"name": "x",
"description": "This is the x parameter"
},
{
"type": "String",
"name": "y",
"description": "This is the y parameter"
},
{
"type": "String",
"name": "z",
"description": "This is the z parameter"
}
]
},
"parameters": [
{
"name": "x",
"splat": false
},
{
"name": "y",
"splat": false
},
{
"name": "z",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "InstanceMethods",
"constant": false,
"value": "{\n\n /*\n Say hi\n @param [String] to the name\n */\n hi: function(to) {},\n\n /*\n Say goodbye\n @param [String] to the name\n */\n goodbye: function(to) {}\n}"
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "hi",
"bound": false,
"documentation": {
"comment": "Say hi",
"summary": "Say hi",
"params": [
{
"type": "String",
"name": "to",
"description": "the name"
}
]
},
"parameters": [
{
"name": "to",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "goodbye",
"bound": false,
"documentation": {
"comment": "Say goodbye",
"summary": "Say goodbye",
"params": [
{
"type": "String",
"name": "to",
"description": "the name"
}
]
},
"parameters": [
{
"name": "to",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"documentation": {
"comment": "",
"summary": "",
"concerns": [
"Example.Mixins.Concern"
]
},
"name": "Example.Concern",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [],
"concerns": [
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "Example.Mixins.Concern",
"concern": true,
"documentation": {
"comment": "This is my concern",
"summary": "This is my concern"
},
"methods": [],
"classMethods": [
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "a",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"params": [
{
"type": "String",
"name": "a",
"description": "This is the a parameter"
},
{
"type": "String",
"name": "b",
"description": "This is the a parameter"
},
{
"type": "String",
"name": "c",
"description": "This is the a parameter"
}
]
},
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
},
{
"name": "c",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "z",
"bound": false,
"documentation": {
"comment": "",
"summary": "",
"params": [
{
"type": "String",
"name": "x",
"description": "This is the x parameter"
},
{
"type": "String",
"name": "y",
"description": "This is the y parameter"
},
{
"type": "String",
"name": "z",
"description": "This is the z parameter"
}
]
},
"parameters": [
{
"name": "x",
"splat": false
},
{
"name": "y",
"splat": false
},
{
"name": "z",
"splat": false
}
]
}
],
"instanceMethods": [
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "hi",
"bound": false,
"documentation": {
"comment": "Say hi",
"summary": "Say hi",
"params": [
{
"type": "String",
"name": "to",
"description": "the name"
}
]
},
"parameters": [
{
"name": "to",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/concern.coffee",
"name": "goodbye",
"bound": false,
"documentation": {
"comment": "Say goodbye",
"summary": "Say goodbye",
"params": [
{
"type": "String",
"name": "to",
"description": "the name"
}
]
},
"parameters": [
{
"name": "to",
"splat": false
}
]
}
],
"variables": []
}
]
}
]
================================================
FILE: spec/_templates/mixins/extend_mixin.coffee
================================================
# @mixin
Name.Space.MyMixin =
hello: ->
goodbye: ->
# @extend Name.Space.MyMixin
class SomeNamespace.MyClass
================================================
FILE: spec/_templates/mixins/extend_mixin.json
================================================
[
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "Name.Space.MyMixin",
"documentation": {
"comment": "",
"summary": ""
},
"methods": [
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "hello",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "goodbye",
"bound": false,
"parameters": []
}
],
"variables": []
},
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "hello",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "goodbye",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"documentation": {
"comment": "",
"summary": "",
"extends": [
"Name.Space.MyMixin"
]
},
"name": "SomeNamespace.MyClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [],
"extends": [
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "Name.Space.MyMixin",
"documentation": {
"comment": "",
"summary": ""
},
"methods": [
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "hello",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/extend_mixin.coffee",
"name": "goodbye",
"bound": false,
"parameters": []
}
],
"variables": []
}
],
"concerns": []
}
]
================================================
FILE: spec/_templates/mixins/include_mixin.coffee
================================================
# @mixin
Name.Space.MyMixin =
hello: ->
goodbye: ->
# @include Name.Space.MyMixin
class SomeNamespace.MyClass
================================================
FILE: spec/_templates/mixins/include_mixin.json
================================================
[
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "Name.Space.MyMixin",
"documentation": {
"comment": "",
"summary": ""
},
"methods": [
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "hello",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "goodbye",
"bound": false,
"parameters": []
}
],
"variables": []
},
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "hello",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "goodbye",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"documentation": {
"comment": "",
"summary": "",
"includes": [
"Name.Space.MyMixin"
]
},
"name": "SomeNamespace.MyClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "Name.Space.MyMixin",
"documentation": {
"comment": "",
"summary": ""
},
"methods": [
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "hello",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/include_mixin.coffee",
"name": "goodbye",
"bound": false,
"parameters": []
}
],
"variables": []
}
],
"extends": [],
"concerns": []
}
]
================================================
FILE: spec/_templates/mixins/missing_mixins.coffee
================================================
# @include Name.Space.MyMixin
# @extend Name.Space.MyMixin
class SomeNamespace.MyClass
================================================
FILE: spec/_templates/mixins/missing_mixins.json
================================================
[
{
"file": "spec/_templates/mixins/missing_mixins.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/mixins/missing_mixins.coffee",
"documentation": {
"comment": "",
"summary": "",
"includes": [
"Name.Space.MyMixin"
],
"extends": [
"Name.Space.MyMixin"
]
},
"name": "SomeNamespace.MyClass",
"methods": [],
"variables": [],
"properties": [],
"includes": [
"Name.Space.MyMixin"
],
"extends": [
"Name.Space.MyMixin"
],
"concerns": []
}
]
================================================
FILE: spec/_templates/mixins/mixin_documentation.coffee
================================================
# This is a test module with `inline.dot`. Beware.
#
# @note Please use
# this carefully
# @note For internal use only
#
# @example Use it in this way
# class Rumba
# @include Foo.Bar
#
# @todo Clean up socket handler
# @todo Refactor
# property factory
# @author Netzpirat
# @author Supermakaka
# @abstract Each listener implementation must include
# @private
# @deprecated Use other module
# which is thread safe
# @since 1.0.0
# @version 1.0.2
#
# @namespace Manual.Namespace
# @mixin
#
Foo.Bar = {}
================================================
FILE: spec/_templates/mixins/mixin_documentation.json
================================================
[
{
"file": "spec/_templates/mixins/mixin_documentation.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/mixins/mixin_documentation.coffee",
"name": "Foo.Bar",
"documentation": {
"comment": "This is a test module with `inline.dot`. Beware.",
"summary": "This is a test module with `inline.dot`.",
"notes": [
"Please use this carefully",
"For internal use only"
],
"abstract": "Each listener implementation must include",
"private": true,
"deprecated": "Use other module which is thread safe",
"version": "1.0.2",
"since": "1.0.0",
"authors": [
"Netzpirat",
"Supermakaka"
],
"todos": [
"Clean up socket handler",
"Refactor property factory"
],
"examples": [
{
"title": "Use it in this way",
"code": "class Rumba\n @include Foo.Bar"
}
],
"namespace": "Manual.Namespace"
},
"methods": [],
"variables": []
}
]
================================================
FILE: spec/_templates/mixins/mixin_methods.coffee
================================================
#
# @method #set(key, value)
# Sets a value
# @param [String] key describe key param
# @param [Object] value describe value param
# @option value [String] string
# @option value [Integer] number
# @option value [Object] whatever
#
# @mixin
Foo =
helper: ->
another: (a, b) ->
withDefault: (a = 2, c, d = 'hi', d, e = { a: 2 }, f = new TestClassMethods()) ->
nowWithSpalt: (foo, bar...) ->
================================================
FILE: spec/_templates/mixins/mixin_methods.json
================================================
[
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "Foo",
"documentation": {
"comment": "",
"summary": "",
"methods": [
{
"signature": "#set(key, value)",
"documentation": {
"params": [
{
"type": "String",
"name": "key",
"description": "describe key param"
},
{
"type": "Object",
"name": "value",
"description": "describe value param"
}
],
"options": {
"value": [
{
"type": "String",
"name": "string"
},
{
"type": "Integer",
"name": "number"
},
{
"type": "Object",
"name": "whatever"
}
]
},
"comment": "Sets a value",
"summary": "Sets a value"
}
}
]
},
"methods": [
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "helper",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "another",
"bound": false,
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "withDefault",
"bound": false,
"parameters": [
{
"name": "a",
"splat": false,
"default": "2"
},
{
"name": "c",
"splat": false
},
{
"name": "d",
"splat": false,
"default": "'hi'"
},
{
"name": "d",
"splat": false
},
{
"name": "e",
"splat": false,
"default": "{\n a: 2\n}"
},
{
"name": "f",
"splat": false,
"default": "new TestClassMethods()"
}
]
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "nowWithSpalt",
"bound": false,
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": true
}
]
}
],
"variables": []
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "helper",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "another",
"bound": false,
"parameters": [
{
"name": "a",
"splat": false
},
{
"name": "b",
"splat": false
}
]
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "withDefault",
"bound": false,
"parameters": [
{
"name": "a",
"splat": false,
"default": "2"
},
{
"name": "c",
"splat": false
},
{
"name": "d",
"splat": false,
"default": "'hi'"
},
{
"name": "d",
"splat": false
},
{
"name": "e",
"splat": false,
"default": "{\n a: 2\n}"
},
{
"name": "f",
"splat": false,
"default": "new TestClassMethods()"
}
]
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "a",
"constant": false,
"value": "2"
},
{
"file": "spec/_templates/mixins/mixin_methods.coffee",
"name": "nowWithSpalt",
"bound": false,
"parameters": [
{
"name": "foo",
"splat": false
},
{
"name": "bar",
"splat": true
}
]
}
]
================================================
FILE: spec/_templates/properties/properties.coffee
================================================
# Property test class. It's almost the most difficult
# part to come up with stupid examples.
#
module.exports = class Person
# @property [Array] The nicknames
#
# All the nicknames the person is known by.
nicknames: []
# @property [Object]
# The entity's position
position:
x: 0
y: 0
# Language helpers
get = (props) => @::__defineGetter__ name, getter for name, getter of props
set = (props) => @::__defineSetter__ name, setter for name, setter of props
# @property [String] The first name
get firstname: -> @_firstname
set firstname: (@_firstname) ->
# The last name
get lastname: -> @_lastname
set lastname: (@_lastname) ->
# @property [Date] The day
# (of birth)
get birth: -> @_birth
# The twitter handle
get twitter: -> @_twitter
# @property [String] The confession
set confession: (@_confession) ->
# @property [String] Something different
@property 'test',
get: -> 'test'
set: (k,v) ->
# @property [String] Something different 2
@property 'test2', -> 'test'
# @property [String] Something different 3
@property 'test3', set: -> 'test'
# The email address offer
set email: (@_email) ->
# This should not be swallowed
test: ->
================================================
FILE: spec/_templates/properties/properties.json
================================================
[
{
"file": "spec/_templates/properties/properties.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"documentation": {
"comment": "Property test class. It's almost the most difficult\npart to come up with stupid examples.",
"summary": "Property test class."
},
"name": "Person",
"methods": [
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test",
"bound": false,
"documentation": {
"comment": "This should not be swallowed",
"summary": "This should not be swallowed"
},
"kind": "dynamic",
"parameters": []
}
],
"variables": [],
"properties": [
{
"file": "spec/_templates/properties/properties.coffee",
"name": "nicknames",
"getter": true,
"setter": true,
"documentation": {
"comment": "The nicknames\n\nAll the nicknames the person is known by.",
"summary": "The nicknames\n\nAll the nicknames the person is known by.",
"property": "Array"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "position",
"getter": true,
"setter": true,
"documentation": {
"comment": "The entity's position",
"summary": "The entity's position",
"property": "Object"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "firstname",
"getter": true,
"setter": true,
"documentation": {
"comment": "The first name",
"summary": "The first name",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "lastname",
"getter": true,
"setter": true,
"documentation": {
"comment": "The last name",
"summary": "The last name"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "birth",
"getter": true,
"setter": false,
"documentation": {
"comment": "The day (of birth)",
"summary": "The day (of birth)",
"property": "Date"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "twitter",
"getter": true,
"setter": false,
"documentation": {
"comment": "The twitter handle",
"summary": "The twitter handle"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "confession",
"getter": false,
"setter": true,
"documentation": {
"comment": "The confession",
"summary": "The confession",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test",
"getter": true,
"setter": true,
"documentation": {
"comment": "Something different",
"summary": "Something different",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test2",
"getter": true,
"setter": false,
"documentation": {
"comment": "Something different 2",
"summary": "Something different 2",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test3",
"getter": false,
"setter": true,
"documentation": {
"comment": "Something different 3",
"summary": "Something different 3",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "email",
"getter": false,
"setter": true,
"documentation": {
"comment": "The email address offer",
"summary": "The email address offer"
}
}
],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "nicknames",
"getter": true,
"setter": true,
"documentation": {
"comment": "The nicknames\n\nAll the nicknames the person is known by.",
"summary": "The nicknames\n\nAll the nicknames the person is known by.",
"property": "Array"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "position",
"getter": true,
"setter": true,
"documentation": {
"comment": "The entity's position",
"summary": "The entity's position",
"property": "Object"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "x",
"constant": false,
"value": "0"
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "y",
"constant": false,
"value": "0"
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "get",
"bound": true,
"documentation": {
"comment": "Language helpers",
"summary": "Language helpers"
},
"parameters": [
{
"name": "props",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "set",
"bound": true,
"parameters": [
{
"name": "props",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "firstname",
"getter": true,
"setter": true,
"documentation": {
"comment": "The first name",
"summary": "The first name",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "firstname",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "firstname",
"getter": true,
"setter": true,
"documentation": {
"comment": "The first name",
"summary": "The first name",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "firstname",
"bound": false,
"parameters": [
{
"name": "_firstname",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "lastname",
"getter": true,
"setter": true,
"documentation": {
"comment": "The last name",
"summary": "The last name"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "lastname",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "lastname",
"getter": true,
"setter": true,
"documentation": {
"comment": "The last name",
"summary": "The last name"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "lastname",
"bound": false,
"parameters": [
{
"name": "_lastname",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "birth",
"getter": true,
"setter": false,
"documentation": {
"comment": "The day (of birth)",
"summary": "The day (of birth)",
"property": "Date"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "birth",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "twitter",
"getter": true,
"setter": false,
"documentation": {
"comment": "The twitter handle",
"summary": "The twitter handle"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "twitter",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "confession",
"getter": false,
"setter": true,
"documentation": {
"comment": "The confession",
"summary": "The confession",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "confession",
"bound": false,
"parameters": [
{
"name": "_confession",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test",
"getter": true,
"setter": true,
"documentation": {
"comment": "Something different",
"summary": "Something different",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "get",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "set",
"bound": false,
"parameters": [
{
"name": "k",
"splat": false
},
{
"name": "v",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test2",
"getter": true,
"setter": false,
"documentation": {
"comment": "Something different 2",
"summary": "Something different 2",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test3",
"getter": false,
"setter": true,
"documentation": {
"comment": "Something different 3",
"summary": "Something different 3",
"property": "String"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "set",
"bound": false,
"parameters": []
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "email",
"getter": false,
"setter": true,
"documentation": {
"comment": "The email address offer",
"summary": "The email address offer"
}
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "email",
"bound": false,
"parameters": [
{
"name": "_email",
"splat": false
}
]
},
{
"file": "spec/_templates/properties/properties.coffee",
"name": "test",
"bound": false,
"documentation": {
"comment": "This should not be swallowed",
"summary": "This should not be swallowed"
},
"kind": "dynamic",
"parameters": []
}
]
================================================
FILE: spec/_templates/variables/class_variables.coffee
================================================
class TestClassVariables
@empty = undefined
@test = 'Hi'
@another = ['a', 'b']
@more =
a: 1
b: 2
================================================
FILE: spec/_templates/variables/class_variables.json
================================================
[
{
"file": "spec/_templates/variables/class_variables.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "TestClassVariables",
"methods": [],
"variables": [
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "empty",
"constant": false,
"value": "undefined",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "test",
"constant": false,
"value": "'Hi'",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "another",
"constant": false,
"value": "['a', 'b']",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "more",
"constant": false,
"value": "{\n a: 1,\n b: 2\n}",
"selfish": true,
"kind": "static"
}
],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "empty",
"constant": false,
"value": "undefined",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "test",
"constant": false,
"value": "'Hi'",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "another",
"constant": false,
"value": "['a', 'b']",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "more",
"constant": false,
"value": "{\n a: 1,\n b: 2\n}",
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "a",
"constant": false,
"value": "1"
},
{
"file": "spec/_templates/variables/class_variables.coffee",
"name": "b",
"constant": false,
"value": "2"
}
]
================================================
FILE: spec/_templates/variables/constant_variables.coffee
================================================
class TestContstantVariables
# Modify it here
@TEST = 'Hi'
@ANOTHER_CONST: ['a', 'b']
================================================
FILE: spec/_templates/variables/constant_variables.json
================================================
[
{
"file": "spec/_templates/variables/constant_variables.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/variables/constant_variables.coffee",
"name": "TestContstantVariables",
"methods": [],
"variables": [
{
"file": "spec/_templates/variables/constant_variables.coffee",
"name": "TEST",
"constant": true,
"value": "'Hi'",
"documentation": {
"comment": "Modify it here",
"summary": "Modify it here"
},
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/constant_variables.coffee",
"name": "ANOTHER_CONST",
"constant": true,
"value": "['a', 'b']",
"selfish": true,
"kind": "static"
}
],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/variables/constant_variables.coffee",
"name": "TEST",
"constant": true,
"value": "'Hi'",
"documentation": {
"comment": "Modify it here",
"summary": "Modify it here"
},
"selfish": true,
"kind": "static"
},
{
"file": "spec/_templates/variables/constant_variables.coffee",
"name": "ANOTHER_CONST",
"constant": true,
"value": "['a', 'b']",
"selfish": true,
"kind": "static"
}
]
================================================
FILE: spec/_templates/variables/instance_variables.coffee
================================================
class TestInstanceVariables
empty: undefined
test: 'Hi'
another: ['a', 'b']
more:
a: 1
b: 2
================================================
FILE: spec/_templates/variables/instance_variables.json
================================================
[
{
"file": "spec/_templates/variables/instance_variables.coffee",
"methods": [],
"variables": []
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "TestInstanceVariables",
"methods": [],
"variables": [
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "empty",
"constant": false,
"value": "undefined",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "test",
"constant": false,
"value": "'Hi'",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "another",
"constant": false,
"value": "['a', 'b']",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "more",
"constant": false,
"value": "{\n a: 1,\n b: 2\n}",
"kind": "dynamic"
}
],
"properties": [],
"includes": [],
"extends": [],
"concerns": []
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "empty",
"constant": false,
"value": "undefined",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "test",
"constant": false,
"value": "'Hi'",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "another",
"constant": false,
"value": "['a', 'b']",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "more",
"constant": false,
"value": "{\n a: 1,\n b: 2\n}",
"kind": "dynamic"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "a",
"constant": false,
"value": "1"
},
{
"file": "spec/_templates/variables/instance_variables.coffee",
"name": "b",
"constant": false,
"value": "2"
}
]
================================================
FILE: spec/lib/api_spec.coffee
================================================
API = require '../../lib/command.coffee'
Path = require 'path'
describe 'API', ->
it 'parses project', (done) ->
api = new API()
api.generate Path.join(__dirname, '../_templates/example'), { test: true }, (err) ->
expect(err).toBeUndefined()
done()
it 'fails if coverage is too low', (done) ->
api = new API()
api.generate Path.join(__dirname, '../_templates/example'), { test: true, "min-coverage": 90 }, (err) ->
expect(err).toBe 1
done()
================================================
FILE: spec/lib/codo_spec.coffee
================================================
Path = require 'path'
Codo = require '../../lib/codo'
describe 'Codo', ->
it 'parses project', ->
environment = Codo.parseProject(Path.join __dirname, '../_templates/example')
expect(environment.allFiles().map (file) -> file.inspect().file).toEqual [
'src/angry_animal.coffee',
'src/animal.coffee',
'src/lion.coffee',
'src/over_documented_class.coffee',
'src/over_documented_mixin.coffee'
].map (file) -> Path.normalize file
expect(environment.allExtras().map (e) -> e.name).toEqual [
'README.md', 'CHANGELOG'
]
expect(environment.options.readme).toEqual 'README.md'
================================================
FILE: spec/lib/entities/class_spec.coffee
================================================
Environment = require '../../../lib/environment'
Method = require '../../../lib/entities/mixin'
describe 'Class', ->
it 'lists effective methods', ->
environment = Environment.read('spec/_templates/complicateds/methods.coffee')
methods = environment.entities[1].effectiveMethods()
expect(methods[0].inspect()).toEqual { name: 'z', kind: 'dynamic', bound: false, parameters: [] }
expect(methods[1].inspect()).toEqual { name : 'x', kind : 'dynamic', parameters : [ 'key', 'value' ] }
it 'lists inherited methods', ->
environment = Environment.read('spec/_templates/complicateds/methods.coffee')
methods = environment.entities[12].inheritedMethods().map (x) -> x.entity.inspect()
expect(methods).toEqual(
[
{ name: 'x', kind: 'dynamic', bound: false, parameters: [ ] },
{ name: 'z', kind: 'dynamic', bound: false, parameters: [ ] },
{ name: 'm', kind: 'dynamic', bound: false, parameters: [ ] },
{ name: 'cs', kind: 'static', bound: false, parameters: [ ] },
{ name: 'cd', kind: 'dynamic', bound: false, parameters: [ ] }
]
)
it 'lists inherited variables', ->
environment = Environment.read('spec/_templates/complicateds/variables.coffee')
variables = environment.entities[5].inheritedVariables().map (x) -> x.entity.inspect()
expect(variables).toEqual(
[
{
file: 'spec/_templates/complicateds/variables.coffee',
name: 'z',
constant: false,
value: "'456'",
kind: 'dynamic'
}
]
)
================================================
FILE: spec/lib/entities/mixin_spec.coffee
================================================
Environment = require '../../../lib/environment'
Method = require '../../../lib/entities/mixin'
describe 'Mixin', ->
describe 'effective methods', ->
it 'get listed for inclusion', ->
environment = Environment.read('spec/_templates/mixins/mixin_methods.coffee')
methods = environment.entities[1].effectiveInclusionMethods().map (m) -> m.inspect()
expect(methods).toEqual(
[
{ name: 'helper', kind: 'dynamic', bound: false, parameters: [] },
{ name: 'another', kind: 'dynamic', bound: false, parameters: [ 'a', 'b' ] },
{
name: 'withDefault',
kind: 'dynamic',
bound: false,
parameters: [
'a = 2',
'c',
'd = \'hi\'',
'd',
'e = {\n a: 2\n}',
'f = new TestClassMethods()'
]
},
{
name: 'nowWithSpalt',
kind: 'dynamic',
bound: false,
parameters: [ 'foo', 'bar...' ]
},
{ name: 'set', kind: 'dynamic', parameters: [ 'key', 'value' ] }
]
)
it 'get listed for extension', ->
environment = Environment.read('spec/_templates/mixins/mixin_methods.coffee')
methods = environment.entities[1].effectiveExtensionMethods().map (m) -> m.inspect()
expect(methods).toEqual(
[
{ name: 'helper', kind: 'static', bound: false, parameters: [] },
{ name: 'another', kind: 'static', bound: false, parameters: [ 'a', 'b' ] },
{
name: 'withDefault',
kind: 'static',
bound: false,
parameters: [
'a = 2',
'c',
'd = \'hi\'',
'd',
'e = {\n a: 2\n}',
'f = new TestClassMethods()'
]
},
{
name: 'nowWithSpalt',
kind: 'static',
bound: false,
parameters: [ 'foo', 'bar...' ]
},
{ name: 'set', kind: 'static', parameters: [ 'key', 'value' ] }
]
)
it 'get listed for concern', ->
environment = Environment.read('spec/_templates/mixins/concern.coffee')
methods = environment.entities[1].effectiveConcernMethods().map (m) -> m.inspect()
expect(methods).toEqual(
[
{ name: 'a', kind: 'static', bound: false, parameters: [ 'a', 'b', 'c' ] },
{ name: 'z', kind: 'static', bound: false, parameters: [ 'x', 'y', 'z' ] },
{ name: 'hi', kind: 'dynamic', bound: false, parameters: [ 'to' ] },
{ name: 'goodbye', kind: 'dynamic', bound: false, parameters: [ 'to' ] } ]
)
================================================
FILE: spec/lib/environment_spec.coffee
================================================
FS = require 'fs'
jsdiff = require 'diff'
Path = require 'path'
walkdir = require 'walkdir'
Environment = require '../../lib/environment'
normalizePathsInObject = (obj) ->
return if typeof obj is "string"
if obj.file? then obj.file = Path.normalize obj.file
for key, val of obj # ['variables', 'methods', 'properties', 'includes', 'container', 'parent', 'extends']
if Array.isArray val
val.forEach normalizePathsInObject
else normalizePathsInObject obj[key]
beforeEach ->
@addMatchers
toTraverseTo: (expected) ->
environment = new Environment
parser = environment.readCoffee(@actual)
environment.linkify()
actual = JSON.parse JSON.stringify environment.inspect()
expected = JSON.parse FS.readFileSync(expected, 'utf8')
expected.forEach (entry) -> normalizePathsInObject entry
diff = ""
for part in jsdiff.diffJson(expected, actual)
if part.added
diff += part.value.green
else if part.removed
diff += part.value.red
else
diff += part.value
diff
@message = ->
report = "\n-------------------- CoffeeScript ----------------------\n"
report += parser.content
report += "\n--------------------- JSON diff -----------------------\n"
report += diff
report += "\n-------------------------------------------------------\n"
require('deep-eql')(expected, actual)
describe 'Environment', ->
describe 'Extras', ->
beforeEach -> @environment = new Environment
it 'reads plain text', ->
@environment.readExtra 'spec/_templates/extras/README'
expect(@environment.allExtras().map (e) -> e.inspect()).toEqual([{
path: 'spec/_templates/extras/README',
parsed: '
blocks on a page.
*/
function initHighlighting() {
if (initHighlighting.called)
return;
initHighlighting.called = true;
var pres = document.getElementsByTagName('pre');
for (var i = 0; i < pres.length; i++) {
var code = findCode(pres[i]);
if (code)
highlightBlock(code, hljs.tabReplace);
}
}
/*
Attaches highlighting to the page load event.
*/
function initHighlightingOnLoad() {
if (window.addEventListener) {
window.addEventListener('DOMContentLoaded', initHighlighting, false);
window.addEventListener('load', initHighlighting, false);
} else if (window.attachEvent)
window.attachEvent('onload', initHighlighting);
else
window.onload = initHighlighting;
}
var languages = {}; // a shortcut to avoid writing "this." everywhere
/* Interface definition */
this.LANGUAGES = languages;
this.highlight = highlight;
this.highlightAuto = highlightAuto;
this.fixMarkup = fixMarkup;
this.highlightBlock = highlightBlock;
this.initHighlighting = initHighlighting;
this.initHighlightingOnLoad = initHighlightingOnLoad;
// Common regexps
this.IDENT_RE = '[a-zA-Z][a-zA-Z0-9_]*';
this.UNDERSCORE_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*';
this.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
this.C_NUMBER_RE = '\\b(0[xX][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float
this.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
this.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
// Common modes
this.BACKSLASH_ESCAPE = {
begin: '\\\\.', relevance: 0
};
this.APOS_STRING_MODE = {
className: 'string',
begin: '\'', end: '\'',
illegal: '\\n',
contains: [this.BACKSLASH_ESCAPE],
relevance: 0
};
this.QUOTE_STRING_MODE = {
className: 'string',
begin: '"', end: '"',
illegal: '\\n',
contains: [this.BACKSLASH_ESCAPE],
relevance: 0
};
this.C_LINE_COMMENT_MODE = {
className: 'comment',
begin: '//', end: '$'
};
this.C_BLOCK_COMMENT_MODE = {
className: 'comment',
begin: '/\\*', end: '\\*/'
};
this.HASH_COMMENT_MODE = {
className: 'comment',
begin: '#', end: '$'
};
this.NUMBER_MODE = {
className: 'number',
begin: this.NUMBER_RE,
relevance: 0
};
this.C_NUMBER_MODE = {
className: 'number',
begin: this.C_NUMBER_RE,
relevance: 0
};
this.BINARY_NUMBER_MODE = {
className: 'number',
begin: this.BINARY_NUMBER_RE,
relevance: 0
};
// Utility functions
this.inherit = function(parent, obj) {
var result = {}
for (var key in parent)
result[key] = parent[key];
if (obj)
for (var key in obj)
result[key] = obj[key];
return result;
}
}();
================================================
FILE: themes/default/assets/javascript/vendor/jquery.js
================================================
/*!
* jQuery JavaScript Library v1.8.1
* http://jquery.com/
*
* Includes Sizzle.js
* http://sizzlejs.com/
*
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: Thu Aug 30 2012 17:17:22 GMT-0400 (Eastern Daylight Time)
*/
(function( window, undefined ) {
var
// A central reference to the root jQuery(document)
rootjQuery,
// The deferred used on DOM ready
readyList,
// Use the correct document accordingly with window argument (sandbox)
document = window.document,
location = window.location,
navigator = window.navigator,
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$,
// Save a reference to some core methods
core_push = Array.prototype.push,
core_slice = Array.prototype.slice,
core_indexOf = Array.prototype.indexOf,
core_toString = Object.prototype.toString,
core_hasOwn = Object.prototype.hasOwnProperty,
core_trim = String.prototype.trim,
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
// Used for matching numbers
core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
// Used for detecting and trimming whitespace
core_rnotwhite = /\S/,
core_rspace = /\s+/,
// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
// A simple way to check for HTML strings
// Prioritize #id over to avoid XSS via location.hash (#9521)
rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
// Match a standalone tag
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
// JSON RegExp
rvalidchars = /^[\],:{}\s]*$/,
rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
// Matches dashed string for camelizing
rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi,
// Used by jQuery.camelCase as callback to replace()
fcamelCase = function( all, letter ) {
return ( letter + "" ).toUpperCase();
},
// The ready event handler and self cleanup method
DOMContentLoaded = function() {
if ( document.addEventListener ) {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
} else if ( document.readyState === "complete" ) {
// we're here because readyState === "complete" in oldIE
// which is good enough for us to call the dom ready!
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
},
// [[Class]] -> type pairs
class2type = {};
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
// Handle $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
// Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
match = rquickExpr.exec( selector );
}
// Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
doc = ( context && context.nodeType ? context.ownerDocument || context : document );
// scripts is true for back-compat
selector = jQuery.parseHTML( match[1], doc, true );
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
this.attr.call( selector, context, true );
}
return jQuery.merge( this, selector );
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}
// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
}
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
},
// Start with an empty selector
selector: "",
// The current version of jQuery being used
jquery: "1.8.1",
// The default length of a jQuery object is 0
length: 0,
// The number of elements contained in the matched element set
size: function() {
return this.length;
},
toArray: function() {
return core_slice.call( this );
},
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num == null ?
// Return a 'clean' array
this.toArray() :
// Return just the object
( num < 0 ? this[ this.length + num ] : this[ num ] );
},
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) {
ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}
// Return the newly-formed element set
return ret;
},
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},
ready: function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
return this;
},
eq: function( i ) {
i = +i;
return i === -1 ?
this.slice( i ) :
this.slice( i, i + 1 );
},
first: function() {
return this.eq( 0 );
},
last: function() {
return this.eq( -1 );
},
slice: function() {
return this.pushStack( core_slice.apply( this, arguments ),
"slice", core_slice.call(arguments).join(",") );
},
map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
end: function() {
return this.prevObject || this.constructor(null);
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: core_push,
sort: [].sort,
splice: [].splice
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// extend jQuery itself if only one argument is passed
if ( length === i ) {
target = this;
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
jQuery.extend({
noConflict: function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
},
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;
} else {
jQuery.ready( true );
}
},
// Handle when the DOM is ready
ready: function( wait ) {
// Abort if there are pending holds or we're already ready
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
}
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}
// Remember that the DOM is ready
jQuery.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
// Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
},
// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
},
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
isWindow: function( obj ) {
return obj != null && obj == obj.window;
},
isNumeric: function( obj ) {
return !isNaN( parseFloat(obj) ) && isFinite( obj );
},
type: function( obj ) {
return obj == null ?
String( obj ) :
class2type[ core_toString.call(obj) ] || "object";
},
isPlainObject: function( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
try {
// Not own constructor property must be Object
if ( obj.constructor &&
!core_hasOwn.call(obj, "constructor") &&
!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
// IE8,9 Will throw exceptions on certain host objects #9897
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || core_hasOwn.call( obj, key );
},
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
error: function( msg ) {
throw new Error( msg );
},
// data: string of html
// context (optional): If specified, the fragment will be created in this context, defaults to document
// scripts (optional): If true, will include scripts passed in the html string
parseHTML: function( data, context, scripts ) {
var parsed;
if ( !data || typeof data !== "string" ) {
return null;
}
if ( typeof context === "boolean" ) {
scripts = context;
context = 0;
}
context = context || document;
// Single tag
if ( (parsed = rsingleTag.exec( data )) ) {
return [ context.createElement( parsed[1] ) ];
}
parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
return jQuery.merge( [],
(parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
},
parseJSON: function( data ) {
if ( !data || typeof data !== "string") {
return null;
}
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = jQuery.trim( data );
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
return window.JSON.parse( data );
}
// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( rvalidchars.test( data.replace( rvalidescape, "@" )
.replace( rvalidtokens, "]" )
.replace( rvalidbraces, "")) ) {
return ( new Function( "return " + data ) )();
}
jQuery.error( "Invalid JSON: " + data );
},
// Cross-browser xml parsing
parseXML: function( data ) {
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;
}
try {
if ( window.DOMParser ) { // Standard
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} else { // IE
xml = new ActiveXObject( "Microsoft.XMLDOM" );
xml.async = "false";
xml.loadXML( data );
}
} catch( e ) {
xml = undefined;
}
if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
},
noop: function() {},
// Evaluates a script in a global context
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
globalEval: function( data ) {
if ( data && core_rnotwhite.test( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
},
// Convert dashed to camelCase; used by the css and data modules
// Microsoft forgot to hump their vendor prefix (#9572)
camelCase: function( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
},
nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
},
// args is for internal usage only
each: function( obj, callback, args ) {
var name,
i = 0,
length = obj.length,
isObj = length === undefined || jQuery.isFunction( obj );
if ( args ) {
if ( isObj ) {
for ( name in obj ) {
if ( callback.apply( obj[ name ], args ) === false ) {
break;
}
}
} else {
for ( ; i < length; ) {
if ( callback.apply( obj[ i++ ], args ) === false ) {
break;
}
}
}
// A special, fast, case for the most common use of each
} else {
if ( isObj ) {
for ( name in obj ) {
if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
break;
}
}
} else {
for ( ; i < length; ) {
if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
break;
}
}
}
}
return obj;
},
// Use native String.trim function wherever possible
trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
function( text ) {
return text == null ?
"" :
core_trim.call( text );
} :
// Otherwise use our own trimming functionality
function( text ) {
return text == null ?
"" :
text.toString().replace( rtrim, "" );
},
// results is for internal usage only
makeArray: function( arr, results ) {
var type,
ret = results || [];
if ( arr != null ) {
// The window, strings (and functions) also have 'length'
// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
type = jQuery.type( arr );
if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
core_push.call( ret, arr );
} else {
jQuery.merge( ret, arr );
}
}
return ret;
},
inArray: function( elem, arr, i ) {
var len;
if ( arr ) {
if ( core_indexOf ) {
return core_indexOf.call( arr, elem, i );
}
len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
}
return -1;
},
merge: function( first, second ) {
var l = second.length,
i = first.length,
j = 0;
if ( typeof l === "number" ) {
for ( ; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
},
grep: function( elems, callback, inv ) {
var retVal,
ret = [],
i = 0,
length = elems.length;
inv = !!inv;
// Go through the array, only saving the items
// that pass the validator function
for ( ; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
}
return ret;
},
// arg is for internal usage only
map: function( elems, callback, arg ) {
var value, key,
ret = [],
i = 0,
length = elems.length,
// jquery objects are treated as arrays
isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
// Go through the array, translating each of the items to their
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret[ ret.length ] = value;
}
}
// Go through every key on the object,
} else {
for ( key in elems ) {
value = callback( elems[ key ], key, arg );
if ( value != null ) {
ret[ ret.length ] = value;
}
}
}
// Flatten any nested arrays
return ret.concat.apply( [], ret );
},
// A global GUID counter for objects
guid: 1,
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function( fn, context ) {
var tmp, args, proxy;
if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
}
// Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// Simulated bind
args = core_slice.call( arguments, 2 );
proxy = function() {
return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
};
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
return proxy;
},
// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
var exec,
bulk = key == null,
i = 0,
length = elems.length;
// Sets many values
if ( key && typeof key === "object" ) {
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
}
chainable = 1;
// Sets one value
} else if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = pass === undefined && jQuery.isFunction( value );
if ( bulk ) {
// Bulk operations only iterate when executing function values
if ( exec ) {
exec = fn;
fn = function( elem, key, value ) {
return exec.call( jQuery( elem ), value );
};
// Otherwise they run against the entire set
} else {
fn.call( elems, value );
fn = null;
}
}
if ( fn ) {
for (; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
}
chainable = 1;
}
return chainable ?
elems :
// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
},
now: function() {
return ( new Date() ).getTime();
}
});
jQuery.ready.promise = function( obj ) {
if ( !readyList ) {
readyList = jQuery.Deferred();
// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready, 1 );
// Standards-based browsers support DOMContentLoaded
} else if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else {
// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
return readyList.promise( obj );
};
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
// All jQuery objects should point back to these
rootjQuery = jQuery(document);
// String to Object options format cache
var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.split( core_rspace ), function( _, flag ) {
object[ flag ] = true;
});
return object;
}
/*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );
var // Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// Flag to know if list is currently firing
firing,
// First callback to fire (used internally by add and fireWith)
firingStart,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// Actual callback list
list = [],
// Stack of fire calls for repeatable lists
stack = !options.once && [],
// Fire callbacks
fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) {
list.push( arg );
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// Control if a given callback is in the list
has: function( fn ) {
return jQuery.inArray( fn, list ) > -1;
},
// Remove all callbacks from the list
empty: function() {
list = [];
return this;
},
// Have the list do nothing anymore
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
disabled: function() {
return !list;
},
// Lock the list in its current state
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// Is it locked?
locked: function() {
return !stack;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( list && ( !fired || stack ) ) {
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
};
return self;
};
jQuery.extend({
Deferred: function( func ) {
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
state = "pending",
promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var action = tuple[ 0 ],
fn = fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
function() {
var returned = fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
}
} :
newDefer[ action ]
);
});
fns = null;
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// Keep pipe for back-compat
promise.pipe = promise.then;
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
// Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}
// deferred[ resolve | reject | notify ] = list.fire
deferred[ tuple[0] ] = list.fire;
deferred[ tuple[0] + "With" ] = list.fireWith;
});
// Make the deferred a promise
promise.promise( deferred );
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
},
// Deferred helper
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = core_slice.call( arguments ),
length = resolveValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
// add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts, resolveValues ) )
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
}
// if we're not waiting on anything, resolve the master
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
});
jQuery.support = (function() {
var support,
all,
a,
select,
opt,
input,
fragment,
eventName,
i,
isSupported,
clickFn,
div = document.createElement("div");
// Preliminary tests
div.setAttribute( "className", "t" );
div.innerHTML = "
a";
all = div.getElementsByTagName("*");
a = div.getElementsByTagName("a")[ 0 ];
a.style.cssText = "top:1px;float:left;opacity:.5";
// Can't get basic test support
if ( !all || !all.length || !a ) {
return {};
}
// First batch of supports tests
select = document.createElement("select");
opt = select.appendChild( document.createElement("option") );
input = div.getElementsByTagName("input")[ 0 ];
support = {
// IE strips leading whitespace when .innerHTML is used
leadingWhitespace: ( div.firstChild.nodeType === 3 ),
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
tbody: !div.getElementsByTagName("tbody").length,
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
htmlSerialize: !!div.getElementsByTagName("link").length,
// Get the style information from getAttribute
// (IE uses .cssText instead)
style: /top/.test( a.getAttribute("style") ),
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
hrefNormalized: ( a.getAttribute("href") === "/a" ),
// Make sure that element opacity exists
// (IE uses filter instead)
// Use a regex to work around a WebKit issue. See #5145
opacity: /^0.5/.test( a.style.opacity ),
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
cssFloat: !!a.style.cssFloat,
// Make sure that if no value is specified for a checkbox
// that it defaults to "on".
// (WebKit defaults to "" instead)
checkOn: ( input.value === "on" ),
// Make sure that a selected-by-default option has a working selected property.
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
optSelected: opt.selected,
// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
getSetAttribute: div.className !== "t",
// Tests for enctype support on a form(#6743)
enctype: !!document.createElement("form").enctype,
// Makes sure cloning an html5 element does not cause problems
// Where outerHTML is undefined, this still works
html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>",
// jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
boxModel: ( document.compatMode === "CSS1Compat" ),
// Will be defined later
submitBubbles: true,
changeBubbles: true,
focusinBubbles: false,
deleteExpando: true,
noCloneEvent: true,
inlineBlockNeedsLayout: false,
shrinkWrapBlocks: false,
reliableMarginRight: true,
boxSizingReliable: true,
pixelPosition: false
};
// Make sure checked status is properly cloned
input.checked = true;
support.noCloneChecked = input.cloneNode( true ).checked;
// Make sure that the options inside disabled selects aren't marked as disabled
// (WebKit marks them as disabled)
select.disabled = true;
support.optDisabled = !opt.disabled;
// Test to see if it's possible to delete an expando from an element
// Fails in Internet Explorer
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
div.attachEvent( "onclick", clickFn = function() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
support.noCloneEvent = false;
});
div.cloneNode( true ).fireEvent("onclick");
div.detachEvent( "onclick", clickFn );
}
// Check if a radio maintains its value
// after being appended to the DOM
input = document.createElement("input");
input.value = "t";
input.setAttribute( "type", "radio" );
support.radioValue = input.value === "t";
input.setAttribute( "checked", "checked" );
// #11217 - WebKit loses check when the name is after the checked attribute
input.setAttribute( "name", "t" );
div.appendChild( input );
fragment = document.createDocumentFragment();
fragment.appendChild( div.lastChild );
// WebKit doesn't clone checked state correctly in fragments
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
support.appendChecked = input.checked;
fragment.removeChild( input );
fragment.appendChild( div );
// Technique from Juriy Zaytsev
// http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
// We only care about the case where non-standard event systems
// are used, namely in IE. Short-circuiting here helps us to
// avoid an eval call (in setAttribute) which can cause CSP
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
if ( div.attachEvent ) {
for ( i in {
submit: true,
change: true,
focusin: true
}) {
eventName = "on" + i;
isSupported = ( eventName in div );
if ( !isSupported ) {
div.setAttribute( eventName, "return;" );
isSupported = ( typeof div[ eventName ] === "function" );
}
support[ i + "Bubbles" ] = isSupported;
}
}
// Run tests that need a body at doc ready
jQuery(function() {
var container, div, tds, marginDiv,
divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
body = document.getElementsByTagName("body")[0];
if ( !body ) {
// Return for frameset docs that don't have a body
return;
}
container = document.createElement("div");
container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
body.insertBefore( container, body.firstChild );
// Construct the test element
div = document.createElement("div");
container.appendChild( div );
// Check if table cells still have offsetWidth/Height when they are set
// to display:none and there are still other visible table cells in a
// table row; if so, offsetWidth/Height are not reliable for use when
// determining if an element has been hidden directly using
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
// (only IE 8 fails this test)
div.innerHTML = "
t
";
tds = div.getElementsByTagName("td");
tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
isSupported = ( tds[ 0 ].offsetHeight === 0 );
tds[ 0 ].style.display = "";
tds[ 1 ].style.display = "none";
// Check if empty table cells still have offsetWidth/Height
// (IE <= 8 fail this test)
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
// Check box-sizing and margin behavior
div.innerHTML = "";
div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
support.boxSizing = ( div.offsetWidth === 4 );
support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
// NOTE: To any future maintainer, we've window.getComputedStyle
// because jsdom on node.js will break without it.
if ( window.getComputedStyle ) {
support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. For more
// info see bug #3333
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
marginDiv = document.createElement("div");
marginDiv.style.cssText = div.style.cssText = divReset;
marginDiv.style.marginRight = marginDiv.style.width = "0";
div.style.width = "1px";
div.appendChild( marginDiv );
support.reliableMarginRight =
!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
}
if ( typeof div.style.zoom !== "undefined" ) {
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
// (IE < 8 does this)
div.innerHTML = "";
div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
// Check if elements with layout shrink-wrap their children
// (IE 6 does this)
div.style.display = "block";
div.style.overflow = "visible";
div.innerHTML = "";
div.firstChild.style.width = "5px";
support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
container.style.zoom = 1;
}
// Null elements to avoid leaks in IE
body.removeChild( container );
container = div = tds = marginDiv = null;
});
// Null elements to avoid leaks in IE
fragment.removeChild( div );
all = a = select = opt = input = fragment = div = null;
return support;
})();
var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
rmultiDash = /([A-Z])/g;
jQuery.extend({
cache: {},
deletedIds: [],
// Please use with caution
uuid: 0,
// Unique for each copy of jQuery on the page
// Non-digits removed to match rinlinejQuery
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
// The following elements throw uncatchable exceptions if you
// attempt to add expando properties to them.
noData: {
"embed": true,
// Ban all objects except for Flash (which handle expandos)
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
"applet": true
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !isEmptyDataObject( elem );
},
data: function( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var thisCache, ret,
internalKey = jQuery.expando,
getByName = typeof name === "string",
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
isNode = elem.nodeType,
// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically
cache = isNode ? jQuery.cache : elem,
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
return;
}
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid;
} else {
id = internalKey;
}
}
if ( !cache[ id ] ) {
cache[ id ] = {};
// Avoids exposing jQuery metadata on plain JS objects when the object
// is serialized using JSON.stringify
if ( !isNode ) {
cache[ id ].toJSON = jQuery.noop;
}
}
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
thisCache = cache[ id ];
// jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
// data.
if ( !pvt ) {
if ( !thisCache.data ) {
thisCache.data = {};
}
thisCache = thisCache.data;
}
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
if ( getByName ) {
// First Try to find as-is property data
ret = thisCache[ name ];
// Test for null|undefined property data
if ( ret == null ) {
// Try to find the camelCased property
ret = thisCache[ jQuery.camelCase( name ) ];
}
} else {
ret = thisCache;
}
return ret;
},
removeData: function( elem, name, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var thisCache, i, l,
isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( !cache[ id ] ) {
return;
}
if ( name ) {
thisCache = pvt ? cache[ id ] : cache[ id ].data;
if ( thisCache ) {
// Support array or space separated string names for data keys
if ( !jQuery.isArray( name ) ) {
// try the string as a key before any manipulation
if ( name in thisCache ) {
name = [ name ];
} else {
// split the camel cased version by spaces unless a key with the spaces exists
name = jQuery.camelCase( name );
if ( name in thisCache ) {
name = [ name ];
} else {
name = name.split(" ");
}
}
}
for ( i = 0, l = name.length; i < l; i++ ) {
delete thisCache[ name[i] ];
}
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return;
}
}
}
// See jQuery.data for more information
if ( !pvt ) {
delete cache[ id ].data;
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
}
// Destroy the cache
if ( isNode ) {
jQuery.cleanData( [ elem ], true );
// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
delete cache[ id ];
// When all else fails, null
} else {
cache[ id ] = null;
}
},
// For internal use only.
_data: function( elem, name, data ) {
return jQuery.data( elem, name, data, true );
},
// A method for determining if a DOM node can handle the data expando
acceptData: function( elem ) {
var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
// nodes accept data unless otherwise specified; rejection can be conditional
return !noData || noData !== true && elem.getAttribute("classid") === noData;
}
});
jQuery.fn.extend({
data: function( key, value ) {
var parts, part, attr, name, l,
elem = this[0],
i = 0,
data = null;
// Gets all values
if ( key === undefined ) {
if ( this.length ) {
data = jQuery.data( elem );
if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
attr = elem.attributes;
for ( l = attr.length; i < l; i++ ) {
name = attr[i].name;
if ( name.indexOf( "data-" ) === 0 ) {
name = jQuery.camelCase( name.substring(5) );
dataAttr( elem, name, data[ name ] );
}
}
jQuery._data( elem, "parsedAttrs", true );
}
}
return data;
}
// Sets multiple values
if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this, key );
});
}
parts = key.split( ".", 2 );
parts[1] = parts[1] ? "." + parts[1] : "";
part = parts[1] + "!";
return jQuery.access( this, function( value ) {
if ( value === undefined ) {
data = this.triggerHandler( "getData" + part, [ parts[0] ] );
// Try to fetch any internally stored data first
if ( data === undefined && elem ) {
data = jQuery.data( elem, key );
data = dataAttr( elem, key, data );
}
return data === undefined && parts[1] ?
this.data( parts[0] ) :
data;
}
parts[1] = value;
this.each(function() {
var self = jQuery( this );
self.triggerHandler( "setData" + part, parts );
jQuery.data( this, key, value );
self.triggerHandler( "changeData" + part, parts );
});
}, null, value, arguments.length > 1, null, false );
},
removeData: function( key ) {
return this.each(function() {
jQuery.removeData( this, key );
});
}
});
function dataAttr( elem, key, data ) {
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
data = elem.getAttribute( name );
if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
// Only convert to a number if it doesn't change the string
+data + "" === data ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
// Make sure we set the data so it isn't changed later
jQuery.data( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
var name;
for ( name in obj ) {
// if the public data object is empty, the private is still empty
if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
continue;
}
if ( name !== "toJSON" ) {
return false;
}
}
return true;
}
jQuery.extend({
queue: function( elem, type, data ) {
var queue;
if ( elem ) {
type = ( type || "fx" ) + "queue";
queue = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !queue || jQuery.isArray(data) ) {
queue = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
queue.push( data );
}
}
return queue || [];
}
},
dequeue: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ),
startLength = queue.length,
fn = queue.shift(),
hooks = jQuery._queueHooks( elem, type ),
next = function() {
jQuery.dequeue( elem, type );
};
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
startLength--;
}
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift( "inprogress" );
}
// clear up the last queue stop function
delete hooks.stop;
fn.call( elem, next, hooks );
}
if ( !startLength && hooks ) {
hooks.empty.fire();
}
},
// not intended for public consumption - generates a queueHooks object, or returns the current one
_queueHooks: function( elem, type ) {
var key = type + "queueHooks";
return jQuery._data( elem, key ) || jQuery._data( elem, key, {
empty: jQuery.Callbacks("once memory").add(function() {
jQuery.removeData( elem, type + "queue", true );
jQuery.removeData( elem, key, true );
})
});
}
});
jQuery.fn.extend({
queue: function( type, data ) {
var setter = 2;
if ( typeof type !== "string" ) {
data = type;
type = "fx";
setter--;
}
if ( arguments.length < setter ) {
return jQuery.queue( this[0], type );
}
return data === undefined ?
this :
this.each(function() {
var queue = jQuery.queue( this, type, data );
// ensure a hooks for this queue
jQuery._queueHooks( this, type );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );
});
},
// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
delay: function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx";
return this.queue( type, function( next, hooks ) {
var timeout = setTimeout( next, time );
hooks.stop = function() {
clearTimeout( timeout );
};
});
},
clearQueue: function( type ) {
return this.queue( type || "fx", [] );
},
// Get a promise resolved when queues of a certain type
// are emptied (fx is the type by default)
promise: function( type, obj ) {
var tmp,
count = 1,
defer = jQuery.Deferred(),
elements = this,
i = this.length,
resolve = function() {
if ( !( --count ) ) {
defer.resolveWith( elements, [ elements ] );
}
};
if ( typeof type !== "string" ) {
obj = type;
type = undefined;
}
type = type || "fx";
while( i-- ) {
tmp = jQuery._data( elements[ i ], type + "queueHooks" );
if ( tmp && tmp.empty ) {
count++;
tmp.empty.add( resolve );
}
}
resolve();
return defer.promise( obj );
}
});
var nodeHook, boolHook, fixSpecified,
rclass = /[\t\r\n]/g,
rreturn = /\r/g,
rtype = /^(?:button|input)$/i,
rfocusable = /^(?:button|input|object|select|textarea)$/i,
rclickable = /^a(?:rea|)$/i,
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
getSetAttribute = jQuery.support.getSetAttribute;
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
},
removeAttr: function( name ) {
return this.each(function() {
jQuery.removeAttr( this, name );
});
},
prop: function( name, value ) {
return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
},
removeProp: function( name ) {
name = jQuery.propFix[ name ] || name;
return this.each(function() {
// try/catch handles cases where IE balks (such as removing a property on window)
try {
this[ name ] = undefined;
delete this[ name ];
} catch( e ) {}
});
},
addClass: function( value ) {
var classNames, i, l, elem,
setClass, c, cl;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).addClass( value.call(this, j, this.className) );
});
}
if ( value && typeof value === "string" ) {
classNames = value.split( core_rspace );
for ( i = 0, l = this.length; i < l; i++ ) {
elem = this[ i ];
if ( elem.nodeType === 1 ) {
if ( !elem.className && classNames.length === 1 ) {
elem.className = value;
} else {
setClass = " " + elem.className + " ";
for ( c = 0, cl = classNames.length; c < cl; c++ ) {
if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
setClass += classNames[ c ] + " ";
}
}
elem.className = jQuery.trim( setClass );
}
}
}
}
return this;
},
removeClass: function( value ) {
var removes, className, elem, c, cl, i, l;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).removeClass( value.call(this, j, this.className) );
});
}
if ( (value && typeof value === "string") || value === undefined ) {
removes = ( value || "" ).split( core_rspace );
for ( i = 0, l = this.length; i < l; i++ ) {
elem = this[ i ];
if ( elem.nodeType === 1 && elem.className ) {
className = (" " + elem.className + " ").replace( rclass, " " );
// loop over each item in the removal list
for ( c = 0, cl = removes.length; c < cl; c++ ) {
// Remove until there is nothing to remove,
while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) {
className = className.replace( " " + removes[ c ] + " " , " " );
}
}
elem.className = value ? jQuery.trim( className ) : "";
}
}
}
return this;
},
toggleClass: function( value, stateVal ) {
var type = typeof value,
isBool = typeof stateVal === "boolean";
if ( jQuery.isFunction( value ) ) {
return this.each(function( i ) {
jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
});
}
return this.each(function() {
if ( type === "string" ) {
// toggle individual class names
var className,
i = 0,
self = jQuery( this ),
state = stateVal,
classNames = value.split( core_rspace );
while ( (className = classNames[ i++ ]) ) {
// check each className given, space separated list
state = isBool ? state : !self.hasClass( className );
self[ state ? "addClass" : "removeClass" ]( className );
}
} else if ( type === "undefined" || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery._data( this, "__className__", this.className );
}
// toggle whole className
this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
}
});
},
hasClass: function( selector ) {
var className = " " + selector + " ",
i = 0,
l = this.length;
for ( ; i < l; i++ ) {
if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
return true;
}
}
return false;
},
val: function( value ) {
var hooks, ret, isFunction,
elem = this[0];
if ( !arguments.length ) {
if ( elem ) {
hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
return ret;
}
ret = elem.value;
return typeof ret === "string" ?
// handle most common string cases
ret.replace(rreturn, "") :
// handle cases where value is null/undef or number
ret == null ? "" : ret;
}
return;
}
isFunction = jQuery.isFunction( value );
return this.each(function( i ) {
var val,
self = jQuery(this);
if ( this.nodeType !== 1 ) {
return;
}
if ( isFunction ) {
val = value.call( this, i, self.val() );
} else {
val = value;
}
// Treat null/undefined as ""; convert numbers to string
if ( val == null ) {
val = "";
} else if ( typeof val === "number" ) {
val += "";
} else if ( jQuery.isArray( val ) ) {
val = jQuery.map(val, function ( value ) {
return value == null ? "" : value + "";
});
}
hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
// If set returns undefined, fall back to normal setting
if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
this.value = val;
}
});
}
});
jQuery.extend({
valHooks: {
option: {
get: function( elem ) {
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932
var val = elem.attributes.value;
return !val || val.specified ? elem.value : elem.text;
}
},
select: {
get: function( elem ) {
var value, i, max, option,
index = elem.selectedIndex,
values = [],
options = elem.options,
one = elem.type === "select-one";
// Nothing was selected
if ( index < 0 ) {
return null;
}
// Loop through all the selected options
i = one ? index : 0;
max = one ? index + 1 : options.length;
for ( ; i < max; i++ ) {
option = options[ i ];
// Don't return options that are disabled or in a disabled optgroup
if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
// Get the specific value for the option
value = jQuery( option ).val();
// We don't need an array for one selects
if ( one ) {
return value;
}
// Multi-Selects return an array
values.push( value );
}
}
// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
if ( one && !values.length && options.length ) {
return jQuery( options[ index ] ).val();
}
return values;
},
set: function( elem, value ) {
var values = jQuery.makeArray( value );
jQuery(elem).find("option").each(function() {
this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
});
if ( !values.length ) {
elem.selectedIndex = -1;
}
return values;
}
}
},
// Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
attrFn: {},
attr: function( elem, name, value, pass ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
return jQuery( elem )[ name ]( value );
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === "undefined" ) {
return jQuery.prop( elem, name, value );
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( notxml ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
return;
} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, "" + value );
return value;
}
} else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
ret = elem.getAttribute( name );
// Non-existent attributes return null, we normalize to undefined
return ret === null ?
undefined :
ret;
}
},
removeAttr: function( elem, value ) {
var propName, attrNames, name, isBool,
i = 0;
if ( value && elem.nodeType === 1 ) {
attrNames = value.split( core_rspace );
for ( ; i < attrNames.length; i++ ) {
name = attrNames[ i ];
if ( name ) {
propName = jQuery.propFix[ name ] || name;
isBool = rboolean.test( name );
// See #9699 for explanation of this approach (setting first, then removal)
// Do not do this for boolean attributes (see #10870)
if ( !isBool ) {
jQuery.attr( elem, name, "" );
}
elem.removeAttribute( getSetAttribute ? name : propName );
// Set corresponding property to false for boolean attributes
if ( isBool && propName in elem ) {
elem[ propName ] = false;
}
}
}
}
},
attrHooks: {
type: {
set: function( elem, value ) {
// We can't allow the type property to be changed (since it causes problems in IE)
if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
jQuery.error( "type property can't be changed" );
} else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
// Setting the type on a radio button after the value resets the value in IE6-9
// Reset value to it's default in case type is set after value
// This is for element creation
var val = elem.value;
elem.setAttribute( "type", value );
if ( val ) {
elem.value = val;
}
return value;
}
}
},
// Use the value property for back compat
// Use the nodeHook for button elements in IE6/7 (#1954)
value: {
get: function( elem, name ) {
if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
return nodeHook.get( elem, name );
}
return name in elem ?
elem.value :
null;
},
set: function( elem, value, name ) {
if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
return nodeHook.set( elem, value, name );
}
// Does not return so that setAttribute is also used
elem.value = value;
}
}
},
propFix: {
tabindex: "tabIndex",
readonly: "readOnly",
"for": "htmlFor",
"class": "className",
maxlength: "maxLength",
cellspacing: "cellSpacing",
cellpadding: "cellPadding",
rowspan: "rowSpan",
colspan: "colSpan",
usemap: "useMap",
frameborder: "frameBorder",
contenteditable: "contentEditable"
},
prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
// Fix name and attach hooks
name = jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];
}
if ( value !== undefined ) {
if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
return ( elem[ name ] = value );
}
} else {
if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
return elem[ name ];
}
}
},
propHooks: {
tabIndex: {
get: function( elem ) {
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
var attributeNode = elem.getAttributeNode("tabindex");
return attributeNode && attributeNode.specified ?
parseInt( attributeNode.value, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
undefined;
}
}
}
});
// Hook for boolean attributes
boolHook = {
get: function( elem, name ) {
// Align boolean attributes with corresponding properties
// Fall back to attribute presence where some booleans are not supported
var attrNode,
property = jQuery.prop( elem, name );
return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
name.toLowerCase() :
undefined;
},
set: function( elem, value, name ) {
var propName;
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else {
// value is true since we know at this point it's type boolean and not false
// Set boolean attributes to the same name and set the DOM property
propName = jQuery.propFix[ name ] || name;
if ( propName in elem ) {
// Only set the IDL specifically if it already exists on the element
elem[ propName ] = true;
}
elem.setAttribute( name, name.toLowerCase() );
}
return name;
}
};
// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !getSetAttribute ) {
fixSpecified = {
name: true,
id: true,
coords: true
};
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
nodeHook = jQuery.valHooks.button = {
get: function( elem, name ) {
var ret;
ret = elem.getAttributeNode( name );
return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
ret.value :
undefined;
},
set: function( elem, value, name ) {
// Set the existing or create a new attribute node
var ret = elem.getAttributeNode( name );
if ( !ret ) {
ret = document.createAttribute( name );
elem.setAttributeNode( ret );
}
return ( ret.value = value + "" );
}
};
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
set: function( elem, value ) {
if ( value === "" ) {
elem.setAttribute( name, "auto" );
return value;
}
}
});
});
// Set contenteditable to false on removals(#10429)
// Setting to empty string throws an error as an invalid value
jQuery.attrHooks.contenteditable = {
get: nodeHook.get,
set: function( elem, value, name ) {
if ( value === "" ) {
value = "false";
}
nodeHook.set( elem, value, name );
}
};
}
// Some attributes require a special call on IE
if ( !jQuery.support.hrefNormalized ) {
jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
get: function( elem ) {
var ret = elem.getAttribute( name, 2 );
return ret === null ? undefined : ret;
}
});
});
}
if ( !jQuery.support.style ) {
jQuery.attrHooks.style = {
get: function( elem ) {
// Return undefined in the case of empty string
// Normalize to lowercase since IE uppercases css property names
return elem.style.cssText.toLowerCase() || undefined;
},
set: function( elem, value ) {
return ( elem.style.cssText = "" + value );
}
};
}
// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !jQuery.support.optSelected ) {
jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
get: function( elem ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
}
}
return null;
}
});
}
// IE6/7 call enctype encoding
if ( !jQuery.support.enctype ) {
jQuery.propFix.enctype = "encoding";
}
// Radios and checkboxes getter/setter
if ( !jQuery.support.checkOn ) {
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = {
get: function( elem ) {
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
return elem.getAttribute("value") === null ? "on" : elem.value;
}
};
});
}
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
}
}
});
});
var rformElems = /^(?:textarea|input|select)$/i,
rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|contextmenu)|click/,
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
hoverHack = function( events ) {
return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
};
/*
* Helper functions for managing events -- not part of the public interface.
* Props to Dean Edwards' addEvent library for many of the ideas.
*/
jQuery.event = {
add: function( elem, types, handler, data, selector ) {
var elemData, eventHandle, events,
t, tns, type, namespaces, handleObj,
handleObjIn, handlers, special;
// Don't attach events to noData or text/comment nodes (allow plain objects tho)
if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
return;
}
// Caller can pass in an object of custom data in lieu of the handler
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
}
// Make sure that the handler has a unique ID, used to find/remove it later
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
// Init the element's event structure and main handler, if this is the first
events = elemData.events;
if ( !events ) {
elemData.events = events = {};
}
eventHandle = elemData.handle;
if ( !eventHandle ) {
elemData.handle = eventHandle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
}
// Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
types = jQuery.trim( hoverHack(types) ).split( " " );
for ( t = 0; t < types.length; t++ ) {
tns = rtypenamespace.exec( types[t] ) || [];
type = tns[1];
namespaces = ( tns[2] || "" ).split( "." ).sort();
// If event changes its type, use the special event handlers for the changed type
special = jQuery.event.special[ type ] || {};
// If selector defined, determine special event api type, otherwise given type
type = ( selector ? special.delegateType : special.bindType ) || type;
// Update special based on newly reset type
special = jQuery.event.special[ type ] || {};
// handleObj is passed to all event handlers
handleObj = jQuery.extend({
type: type,
origType: tns[1],
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
namespace: namespaces.join(".")
}, handleObjIn );
// Init the event handler queue if we're the first
handlers = events[ type ];
if ( !handlers ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0;
// Only use addEventListener/attachEvent if the special events handler returns false
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
if ( special.add ) {
special.add.call( elem, handleObj );
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// Add to the element's handler list, delegates in front
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
// Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;
}
// Nullify elem to prevent memory leaks in IE
elem = null;
},
global: {},
// Detach an event or set of events from an element
remove: function( elem, types, handler, selector, mappedTypes ) {
var t, tns, type, origType, namespaces, origCount,
j, events, special, eventType, handleObj,
elemData = jQuery.hasData( elem ) && jQuery._data( elem );
if ( !elemData || !(events = elemData.events) ) {
return;
}
// Once for each type.namespace in types; type may be omitted
types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
for ( t = 0; t < types.length; t++ ) {
tns = rtypenamespace.exec( types[t] ) || [];
type = origType = tns[1];
namespaces = tns[2];
// Unbind all events (on this namespace, if provided) for the element
if ( !type ) {
for ( type in events ) {
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
}
continue;
}
special = jQuery.event.special[ type ] || {};
type = ( selector? special.delegateType : special.bindType ) || type;
eventType = events[ type ] || [];
origCount = eventType.length;
namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
// Remove matching events
for ( j = 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
if ( ( mappedTypes || origType === handleObj.origType ) &&
( !handler || handler.guid === handleObj.guid ) &&
( !namespaces || namespaces.test( handleObj.namespace ) ) &&
( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
eventType.splice( j--, 1 );
if ( handleObj.selector ) {
eventType.delegateCount--;
}
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
}
}
// Remove generic event handler if we removed something and no more handlers exist
// (avoids potential for endless recursion during removal of special event handlers)
if ( eventType.length === 0 && origCount !== eventType.length ) {
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
delete events[ type ];
}
}
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
delete elemData.handle;
// removeData also checks for emptiness and clears the expando if empty
// so use it instead of delete
jQuery.removeData( elem, "events", true );
}
},
// Events that are safe to short-circuit if no handlers are attached.
// Native DOM events should not be added, they may have inline handlers.
customEvent: {
"getData": true,
"setData": true,
"changeData": true
},
trigger: function( event, data, elem, onlyHandlers ) {
// Don't do events on text and comment nodes
if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
return;
}
// Event object or event type
var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
type = event.type || event,
namespaces = [];
// focus/blur morphs to focusin/out; ensure we're not firing them right now
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
}
if ( type.indexOf( "!" ) >= 0 ) {
// Exclusive events trigger only for the exact event (no namespaces)
type = type.slice(0, -1);
exclusive = true;
}
if ( type.indexOf( "." ) >= 0 ) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split(".");
type = namespaces.shift();
namespaces.sort();
}
if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
// No jQuery handlers for this event type, and it can't have inline handlers
return;
}
// Caller can pass in an Event, Object, or just an event type string
event = typeof event === "object" ?
// jQuery.Event object
event[ jQuery.expando ] ? event :
// Object literal
new jQuery.Event( type, event ) :
// Just the event type (string)
new jQuery.Event( type );
event.type = type;
event.isTrigger = true;
event.exclusive = exclusive;
event.namespace = namespaces.join( "." );
event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
// Handle a global trigger
if ( !elem ) {
// TODO: Stop taunting the data cache; remove global events and always attach to document
cache = jQuery.cache;
for ( i in cache ) {
if ( cache[ i ].events && cache[ i ].events[ type ] ) {
jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
}
}
return;
}
// Clean up the event in case it is being reused
event.result = undefined;
if ( !event.target ) {
event.target = elem;
}
// Clone any incoming data and prepend the event, creating the handler arg list
data = data != null ? jQuery.makeArray( data ) : [];
data.unshift( event );
// Allow special events to draw outside the lines
special = jQuery.event.special[ type ] || {};
if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
// Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
eventPath = [[ elem, special.bindType || type ]];
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
bubbleType = special.delegateType || type;
cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
for ( old = elem; cur; cur = cur.parentNode ) {
eventPath.push([ cur, bubbleType ]);
old = cur;
}
// Only add window if we got to document (e.g., not plain obj or detached DOM)
if ( old === (elem.ownerDocument || document) ) {
eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
}
}
// Fire handlers on the event path
for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
cur = eventPath[i][0];
event.type = eventPath[i][1];
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
// Note that this is a bare JS function and not a jQuery handler
handle = ontype && cur[ ontype ];
if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
}
event.type = type;
// If nobody prevented the default action, do it now
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
// Call a native DOM method on the target with the same name name as the event.
// Can't use an .isFunction() check here because IE6/7 fails that test.
// Don't do default actions on window, that's where global variables be (#6170)
// IE<9 dies on focus/blur to hidden element (#1486)
if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
// Don't re-trigger an onFOO event when we call its FOO() method
old = elem[ ontype ];
if ( old ) {
elem[ ontype ] = null;
}
// Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
elem[ type ]();
jQuery.event.triggered = undefined;
if ( old ) {
elem[ ontype ] = old;
}
}
}
}
return event.result;
},
dispatch: function( event ) {
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event || window.event );
var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,
handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
delegateCount = handlers.delegateCount,
args = [].slice.call( arguments ),
run_all = !event.exclusive && !event.namespace,
special = jQuery.event.special[ event.type ] || {},
handlerQueue = [];
// Use the fix-ed jQuery.Event rather than the (read-only) native event
args[0] = event;
event.delegateTarget = this;
// Call the preDispatch hook for the mapped type, and let it bail if desired
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
return;
}
// Determine handlers that should run if there are delegated events
// Avoid non-left-click bubbling in Firefox (#3861)
if ( delegateCount && !(event.button && event.type === "click") ) {
for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
// Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.disabled !== true || event.type !== "click" ) {
selMatch = {};
matches = [];
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
sel = handleObj.selector;
if ( selMatch[ sel ] === undefined ) {
selMatch[ sel ] = jQuery( sel, this ).index( cur ) >= 0;
}
if ( selMatch[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
handlerQueue.push({ elem: cur, matches: matches });
}
}
}
}
// Add the remaining (directly-bound) handlers
if ( handlers.length > delegateCount ) {
handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
}
// Run delegates first; they may want to stop propagation beneath us
for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
matched = handlerQueue[ i ];
event.currentTarget = matched.elem;
for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
handleObj = matched.matches[ j ];
// Triggered event must either 1) be non-exclusive and have no namespace, or
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
event.data = handleObj.data;
event.handleObj = handleObj;
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
.apply( matched.elem, args );
if ( ret !== undefined ) {
event.result = ret;
if ( ret === false ) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
// Call the postDispatch hook for the mapped type
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
}
return event.result;
},
// Includes some event props shared by KeyEvent and MouseEvent
// *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
fixHooks: {},
keyHooks: {
props: "char charCode key keyCode".split(" "),
filter: function( event, original ) {
// Add which for key events
if ( event.which == null ) {
event.which = original.charCode != null ? original.charCode : original.keyCode;
}
return event;
}
},
mouseHooks: {
props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
filter: function( event, original ) {
var eventDoc, doc, body,
button = original.button,
fromElement = original.fromElement;
// Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && original.clientX != null ) {
eventDoc = event.target.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
}
// Add relatedTarget, if necessary
if ( !event.relatedTarget && fromElement ) {
event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
}
// Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
if ( !event.which && button !== undefined ) {
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
}
return event;
}
},
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
// Create a writable copy of the event object and normalize some properties
var i, prop,
originalEvent = event,
fixHook = jQuery.event.fixHooks[ event.type ] || {},
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
event = jQuery.Event( originalEvent );
for ( i = copy.length; i; ) {
prop = copy[ --i ];
event[ prop ] = originalEvent[ prop ];
}
// Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
if ( !event.target ) {
event.target = originalEvent.srcElement || document;
}
// Target should not be a text node (#504, Safari)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
// For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
event.metaKey = !!event.metaKey;
return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
},
special: {
load: {
// Prevent triggered image.load events from bubbling to window.load
noBubble: true
},
focus: {
delegateType: "focusin"
},
blur: {
delegateType: "focusout"
},
beforeunload: {
setup: function( data, namespaces, eventHandle ) {
// We only want to do this special case on windows
if ( jQuery.isWindow( this ) ) {
this.onbeforeunload = eventHandle;
}
},
teardown: function( namespaces, eventHandle ) {
if ( this.onbeforeunload === eventHandle ) {
this.onbeforeunload = null;
}
}
}
},
simulate: function( type, elem, event, bubble ) {
// Piggyback on a donor event to simulate a different one.
// Fake originalEvent to avoid donor's stopPropagation, but if the
// simulated event prevents default then we do the same on the donor.
var e = jQuery.extend(
new jQuery.Event(),
event,
{ type: type,
isSimulated: true,
originalEvent: {}
}
);
if ( bubble ) {
jQuery.event.trigger( e, null, elem );
} else {
jQuery.event.dispatch.call( elem, e );
}
if ( e.isDefaultPrevented() ) {
event.preventDefault();
}
}
};
// Some plugins are using, but it's undocumented/deprecated and will be removed.
// The 1.7 special event interface should provide all the hooks needed now.
jQuery.event.handle = jQuery.event.dispatch;
jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
var name = "on" + type;
if ( elem.detachEvent ) {
// #8545, #7054, preventing memory leaks for custom events in IE6-8 –
// detachEvent needed property on element, by name of that event, to properly expose it to GC
if ( typeof elem[ name ] === "undefined" ) {
elem[ name ] = null;
}
elem.detachEvent( name, handle );
}
};
jQuery.Event = function( src, props ) {
// Allow instantiation without the 'new' keyword
if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
}
// Event object
if ( src && src.type ) {
this.originalEvent = src;
this.type = src.type;
// Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
// Event type
} else {
this.type = src;
}
// Put explicitly provided properties onto the event object
if ( props ) {
jQuery.extend( this, props );
}
// Create a timestamp if incoming event doesn't have one
this.timeStamp = src && src.timeStamp || jQuery.now();
// Mark it as fixed
this[ jQuery.expando ] = true;
};
function returnFalse() {
return false;
}
function returnTrue() {
return true;
}
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
preventDefault: function() {
this.isDefaultPrevented = returnTrue;
var e = this.originalEvent;
if ( !e ) {
return;
}
// if preventDefault exists run it on the original event
if ( e.preventDefault ) {
e.preventDefault();
// otherwise set the returnValue property of the original event to false (IE)
} else {
e.returnValue = false;
}
},
stopPropagation: function() {
this.isPropagationStopped = returnTrue;
var e = this.originalEvent;
if ( !e ) {
return;
}
// if stopPropagation exists run it on the original event
if ( e.stopPropagation ) {
e.stopPropagation();
}
// otherwise set the cancelBubble property of the original event to true (IE)
e.cancelBubble = true;
},
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
},
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse
};
// Create mouseenter/leave events using mouseover/out and event-time checks
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
delegateType: fix,
bindType: fix,
handle: function( event ) {
var ret,
target = this,
related = event.relatedTarget,
handleObj = event.handleObj,
selector = handleObj.selector;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
event.type = handleObj.origType;
ret = handleObj.handler.apply( this, arguments );
event.type = fix;
}
return ret;
}
};
});
// IE submit delegation
if ( !jQuery.support.submitBubbles ) {
jQuery.event.special.submit = {
setup: function() {
// Only need this for delegated form submit events
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// Lazy-add a submit handler when a descendant form may potentially be submitted
jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
// Node name check avoids a VML-related crash in IE (#9807)
var elem = e.target,
form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
if ( form && !jQuery._data( form, "_submit_attached" ) ) {
jQuery.event.add( form, "submit._submit", function( event ) {
event._submit_bubble = true;
});
jQuery._data( form, "_submit_attached", true );
}
});
// return undefined since we don't need an event listener
},
postDispatch: function( event ) {
// If form was submitted by the user, bubble the event up the tree
if ( event._submit_bubble ) {
delete event._submit_bubble;
if ( this.parentNode && !event.isTrigger ) {
jQuery.event.simulate( "submit", this.parentNode, event, true );
}
}
},
teardown: function() {
// Only need this for delegated form submit events
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
jQuery.event.remove( this, "._submit" );
}
};
}
// IE change delegation and checkbox/radio fix
if ( !jQuery.support.changeBubbles ) {
jQuery.event.special.change = {
setup: function() {
if ( rformElems.test( this.nodeName ) ) {
// IE doesn't fire change on a check/radio until blur; trigger it on click
// after a propertychange. Eat the blur-change in special.change.handle.
// This still fires onchange a second time for check/radio after blur.
if ( this.type === "checkbox" || this.type === "radio" ) {
jQuery.event.add( this, "propertychange._change", function( event ) {
if ( event.originalEvent.propertyName === "checked" ) {
this._just_changed = true;
}
});
jQuery.event.add( this, "click._change", function( event ) {
if ( this._just_changed && !event.isTrigger ) {
this._just_changed = false;
}
// Allow triggered, simulated change events (#11500)
jQuery.event.simulate( "change", this, event, true );
});
}
return false;
}
// Delegated event; lazy-add a change handler on descendant inputs
jQuery.event.add( this, "beforeactivate._change", function( e ) {
var elem = e.target;
if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
jQuery.event.add( elem, "change._change", function( event ) {
if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
jQuery.event.simulate( "change", this.parentNode, event, true );
}
});
jQuery._data( elem, "_change_attached", true );
}
});
},
handle: function( event ) {
var elem = event.target;
// Swallow native change events from checkbox/radio, we already triggered them above
if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
return event.handleObj.handler.apply( this, arguments );
}
},
teardown: function() {
jQuery.event.remove( this, "._change" );
return !rformElems.test( this.nodeName );
}
};
}
// Create "bubbling" focus and blur events
if ( !jQuery.support.focusinBubbles ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
// Attach a single capturing handler while someone wants focusin/focusout
var attaches = 0,
handler = function( event ) {
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
};
jQuery.event.special[ fix ] = {
setup: function() {
if ( attaches++ === 0 ) {
document.addEventListener( orig, handler, true );
}
},
teardown: function() {
if ( --attaches === 0 ) {
document.removeEventListener( orig, handler, true );
}
}
};
});
}
jQuery.fn.extend({
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var origFn, type;
// Types can be a map of types/handlers
if ( typeof types === "object" ) {
// ( types-Object, selector, data )
if ( typeof selector !== "string" ) { // && selector != null
// ( types-Object, data )
data = data || selector;
selector = undefined;
}
for ( type in types ) {
this.on( type, selector, data, types[ type ], one );
}
return this;
}
if ( data == null && fn == null ) {
// ( types, fn )
fn = selector;
data = selector = undefined;
} else if ( fn == null ) {
if ( typeof selector === "string" ) {
// ( types, selector, fn )
fn = data;
data = undefined;
} else {
// ( types, data, fn )
fn = data;
data = selector;
selector = undefined;
}
}
if ( fn === false ) {
fn = returnFalse;
} else if ( !fn ) {
return this;
}
if ( one === 1 ) {
origFn = fn;
fn = function( event ) {
// Can use an empty set, since event contains the info
jQuery().off( event );
return origFn.apply( this, arguments );
};
// Use same guid so caller can remove using origFn
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
return this.each( function() {
jQuery.event.add( this, types, fn, data, selector );
});
},
one: function( types, selector, data, fn ) {
return this.on( types, selector, data, fn, 1 );
},
off: function( types, selector, fn ) {
var handleObj, type;
if ( types && types.preventDefault && types.handleObj ) {
// ( event ) dispatched jQuery.Event
handleObj = types.handleObj;
jQuery( types.delegateTarget ).off(
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
handleObj.selector,
handleObj.handler
);
return this;
}
if ( typeof types === "object" ) {
// ( types-object [, selector] )
for ( type in types ) {
this.off( type, selector, types[ type ] );
}
return this;
}
if ( selector === false || typeof selector === "function" ) {
// ( types [, fn] )
fn = selector;
selector = undefined;
}
if ( fn === false ) {
fn = returnFalse;
}
return this.each(function() {
jQuery.event.remove( this, types, fn, selector );
});
},
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
return this.off( types, null, fn );
},
live: function( types, data, fn ) {
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
},
die: function( types, fn ) {
jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
},
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {
// ( namespace ) or ( selector, types [, fn] )
return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
},
trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
triggerHandler: function( type, data ) {
if ( this[0] ) {
return jQuery.event.trigger( type, data, this[0], true );
}
},
toggle: function( fn ) {
// Save reference to arguments for access in closure
var args = arguments,
guid = fn.guid || jQuery.guid++,
i = 0,
toggler = function( event ) {
// Figure out which function to execute
var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
// Make sure that clicks stop
event.preventDefault();
// and execute the function
return args[ lastToggle ].apply( this, arguments ) || false;
};
// link all the functions, so any of them can unbind this click handler
toggler.guid = guid;
while ( i < args.length ) {
args[ i++ ].guid = guid;
}
return this.click( toggler );
},
hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
}
});
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
// Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
if ( fn == null ) {
fn = data;
data = null;
}
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
if ( rkeyEvent.test( name ) ) {
jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
}
if ( rmouseEvent.test( name ) ) {
jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
}
});
/*!
* Sizzle CSS Selector Engine
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license
* http://sizzlejs.com/
*/
(function( window, undefined ) {
var dirruns,
cachedruns,
assertGetIdNotName,
Expr,
getText,
isXML,
contains,
compile,
sortOrder,
hasDuplicate,
baseHasDuplicate = true,
strundefined = "undefined",
expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
document = window.document,
docElem = document.documentElement,
done = 0,
slice = [].slice,
push = [].push,
// Augment a function for special use by Sizzle
markFunction = function( fn, value ) {
fn[ expando ] = value || true;
return fn;
},
createCache = function() {
var cache = {},
keys = [];
return markFunction(function( key, value ) {
// Only keep the most recent entries
if ( keys.push( key ) > Expr.cacheLength ) {
delete cache[ keys.shift() ];
}
return (cache[ key ] = value);
}, cache );
},
classCache = createCache(),
tokenCache = createCache(),
compilerCache = createCache(),
// Regex
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
whitespace = "[\\x20\\t\\r\\n\\f]",
// http://www.w3.org/TR/css3-syntax/#characters
characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
// Loosely modeled on CSS identifier characters
// An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
identifier = characterEncoding.replace( "w", "w#" ),
// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
operators = "([*^$|!~]?=)",
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
// Prefer arguments not in parens/brackets,
// then attribute selectors and non-pseudos (denoted by :),
// then anything else
// These preferences are here to reduce the number of selectors
// needing tokenize in the PSEUDO preFilter
pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
// For matchExpr.POS and matchExpr.needsContext
pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\(((?:-\\d)?\\d*)\\)|)(?=[^-]|$)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
rpseudo = new RegExp( pseudos ),
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
rnot = /^:not/,
rsibling = /[\x20\t\r\n\f]*[+~]/,
rendsWithNot = /:not\($/,
rheader = /h\d/i,
rinputs = /input|select|textarea|button/i,
rbackslash = /\\(?!\\)/g,
matchExpr = {
"ID": new RegExp( "^#(" + characterEncoding + ")" ),
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
"CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace +
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"POS": new RegExp( pos, "ig" ),
// For use in libraries implementing .is()
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
},
// Support
// Used for testing something on an element
assert = function( fn ) {
var div = document.createElement("div");
try {
return fn( div );
} catch (e) {
return false;
} finally {
// release memory in IE
div = null;
}
},
// Check if getElementsByTagName("*") returns only elements
assertTagNameNoComments = assert(function( div ) {
div.appendChild( document.createComment("") );
return !div.getElementsByTagName("*").length;
}),
// Check if getAttribute returns normalized href attributes
assertHrefNotNormalized = assert(function( div ) {
div.innerHTML = "";
return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
div.firstChild.getAttribute("href") === "#";
}),
// Check if attributes should be retrieved by attribute nodes
assertAttributes = assert(function( div ) {
div.innerHTML = "";
var type = typeof div.lastChild.getAttribute("multiple");
// IE8 returns a string for some attributes even when not present
return type !== "boolean" && type !== "string";
}),
// Check if getElementsByClassName can be trusted
assertUsableClassName = assert(function( div ) {
// Opera can't find a second classname (in 9.6)
div.innerHTML = "";
if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
return false;
}
// Safari 3.2 caches class attributes and doesn't catch changes
div.lastChild.className = "e";
return div.getElementsByClassName("e").length === 2;
}),
// Check if getElementById returns elements by name
// Check if getElementsByName privileges form controls or returns elements by ID
assertUsableName = assert(function( div ) {
// Inject content
div.id = expando + 0;
div.innerHTML = "";
docElem.insertBefore( div, docElem.firstChild );
// Test
var pass = document.getElementsByName &&
// buggy browsers will return fewer than the correct 2
document.getElementsByName( expando ).length === 2 +
// buggy browsers will return more than the correct 0
document.getElementsByName( expando + 0 ).length;
assertGetIdNotName = !document.getElementById( expando );
// Cleanup
docElem.removeChild( div );
return pass;
});
// If slice is not available, provide a backup
try {
slice.call( docElem.childNodes, 0 )[0].nodeType;
} catch ( e ) {
slice = function( i ) {
var elem, results = [];
for ( ; (elem = this[i]); i++ ) {
results.push( elem );
}
return results;
};
}
function Sizzle( selector, context, results, seed ) {
results = results || [];
context = context || document;
var match, elem, xml, m,
nodeType = context.nodeType;
if ( nodeType !== 1 && nodeType !== 9 ) {
return [];
}
if ( !selector || typeof selector !== "string" ) {
return results;
}
xml = isXML( context );
if ( !xml && !seed ) {
if ( (match = rquickExpr.exec( selector )) ) {
// Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
elem = context.getElementById( m );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
contains( context, elem ) && elem.id === m ) {
results.push( elem );
return results;
}
}
// Speed-up: Sizzle("TAG")
} else if ( match[2] ) {
push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
return results;
// Speed-up: Sizzle(".CLASS")
} else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
return results;
}
}
}
// All others
return select( selector, context, results, seed, xml );
}
Sizzle.matches = function( expr, elements ) {
return Sizzle( expr, null, null, elements );
};
Sizzle.matchesSelector = function( elem, expr ) {
return Sizzle( expr, null, null, [ elem ] ).length > 0;
};
// Returns a function to use in pseudos for input types
function createInputPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === type;
};
}
// Returns a function to use in pseudos for buttons
function createButtonPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && elem.type === type;
};
}
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if ( nodeType ) {
if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (see #11153)
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
} else {
// If no nodeType, this is expected to be an array
for ( ; (node = elem[i]); i++ ) {
// Do not traverse comment nodes
ret += getText( node );
}
}
return ret;
};
isXML = Sizzle.isXML = function isXML( elem ) {
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
// Element contains another
contains = Sizzle.contains = docElem.contains ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
} :
docElem.compareDocumentPosition ?
function( a, b ) {
return b && !!( a.compareDocumentPosition( b ) & 16 );
} :
function( a, b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
return false;
};
Sizzle.attr = function( elem, name ) {
var attr,
xml = isXML( elem );
if ( !xml ) {
name = name.toLowerCase();
}
if ( Expr.attrHandle[ name ] ) {
return Expr.attrHandle[ name ]( elem );
}
if ( assertAttributes || xml ) {
return elem.getAttribute( name );
}
attr = elem.getAttributeNode( name );
return attr ?
typeof elem[ name ] === "boolean" ?
elem[ name ] ? name : null :
attr.specified ? attr.value : null :
null;
};
Expr = Sizzle.selectors = {
// Can be adjusted by the user
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
order: new RegExp( "ID|TAG" +
(assertUsableName ? "|NAME" : "") +
(assertUsableClassName ? "|CLASS" : "")
),
// IE6/7 return a modified href
attrHandle: assertHrefNotNormalized ?
{} :
{
"href": function( elem ) {
return elem.getAttribute( "href", 2 );
},
"type": function( elem ) {
return elem.getAttribute("type");
}
},
find: {
"ID": assertGetIdNotName ?
function( id, context, xml ) {
if ( typeof context.getElementById !== strundefined && !xml ) {
var m = context.getElementById( id );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : [];
}
} :
function( id, context, xml ) {
if ( typeof context.getElementById !== strundefined && !xml ) {
var m = context.getElementById( id );
return m ?
m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
[m] :
undefined :
[];
}
},
"TAG": assertTagNameNoComments ?
function( tag, context ) {
if ( typeof context.getElementsByTagName !== strundefined ) {
return context.getElementsByTagName( tag );
}
} :
function( tag, context ) {
var results = context.getElementsByTagName( tag );
// Filter out possible comments
if ( tag === "*" ) {
var elem,
tmp = [],
i = 0;
for ( ; (elem = results[i]); i++ ) {
if ( elem.nodeType === 1 ) {
tmp.push( elem );
}
}
return tmp;
}
return results;
},
"NAME": function( tag, context ) {
if ( typeof context.getElementsByName !== strundefined ) {
return context.getElementsByName( name );
}
},
"CLASS": function( className, context, xml ) {
if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
return context.getElementsByClassName( className );
}
}
},
relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
},
preFilter: {
"ATTR": function( match ) {
match[1] = match[1].replace( rbackslash, "" );
// Move the given value to match[3] whether quoted or unquoted
match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
if ( match[2] === "~=" ) {
match[3] = " " + match[3] + " ";
}
return match.slice( 0, 4 );
},
"CHILD": function( match ) {
/* matches from matchExpr.CHILD
1 type (only|nth|...)
2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
3 xn-component of xn+y argument ([+-]?\d*n|)
4 sign of xn-component
5 x of xn-component
6 sign of y-component
7 y of y-component
*/
match[1] = match[1].toLowerCase();
if ( match[1] === "nth" ) {
// nth-child requires argument
if ( !match[2] ) {
Sizzle.error( match[0] );
}
// numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
// other types prohibit arguments
} else if ( match[2] ) {
Sizzle.error( match[0] );
}
return match;
},
"PSEUDO": function( match, context, xml ) {
var unquoted, excess;
if ( matchExpr["CHILD"].test( match[0] ) ) {
return null;
}
if ( match[3] ) {
match[2] = match[3];
} else if ( (unquoted = match[4]) ) {
// Only check arguments that contain a pseudo
if ( rpseudo.test(unquoted) &&
// Get excess from tokenize (recursively)
(excess = tokenize( unquoted, context, xml, true )) &&
// advance to the next closing parenthesis
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
// excess is a negative index
unquoted = unquoted.slice( 0, excess );
match[0] = match[0].slice( 0, excess );
}
match[2] = unquoted;
}
// Return only captures needed by the pseudo filter method (type and argument)
return match.slice( 0, 3 );
}
},
filter: {
"ID": assertGetIdNotName ?
function( id ) {
id = id.replace( rbackslash, "" );
return function( elem ) {
return elem.getAttribute("id") === id;
};
} :
function( id ) {
id = id.replace( rbackslash, "" );
return function( elem ) {
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
return node && node.value === id;
};
},
"TAG": function( nodeName ) {
if ( nodeName === "*" ) {
return function() { return true; };
}
nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
return function( elem ) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
};
},
"CLASS": function( className ) {
var pattern = classCache[ expando ][ className ];
if ( !pattern ) {
pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") );
}
return function( elem ) {
return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
};
},
"ATTR": function( name, operator, check ) {
if ( !operator ) {
return function( elem ) {
return Sizzle.attr( elem, name ) != null;
};
}
return function( elem ) {
var result = Sizzle.attr( elem, name ),
value = result + "";
if ( result == null ) {
return operator === "!=";
}
switch ( operator ) {
case "=":
return value === check;
case "!=":
return value !== check;
case "^=":
return check && value.indexOf( check ) === 0;
case "*=":
return check && value.indexOf( check ) > -1;
case "$=":
return check && value.substr( value.length - check.length ) === check;
case "~=":
return ( " " + value + " " ).indexOf( check ) > -1;
case "|=":
return value === check || value.substr( 0, check.length + 1 ) === check + "-";
}
};
},
"CHILD": function( type, argument, first, last ) {
if ( type === "nth" ) {
var doneName = done++;
return function( elem ) {
var parent, diff,
count = 0,
node = elem;
if ( first === 1 && last === 0 ) {
return true;
}
parent = elem.parentNode;
if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) {
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.sizset = ++count;
if ( node === elem ) {
break;
}
}
}
parent[ expando ] = doneName;
}
diff = elem.sizset - last;
if ( first === 0 ) {
return diff === 0;
} else {
return ( diff % first === 0 && diff / first >= 0 );
}
};
}
return function( elem ) {
var node = elem;
switch ( type ) {
case "only":
case "first":
while ( (node = node.previousSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
if ( type === "first" ) {
return true;
}
node = elem;
/* falls through */
case "last":
while ( (node = node.nextSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
return true;
}
};
},
"PSEUDO": function( pseudo, argument, context, xml ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
var args,
fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ];
if ( !fn ) {
Sizzle.error( "unsupported pseudo: " + pseudo );
}
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( !fn[ expando ] ) {
if ( fn.length > 1 ) {
args = [ pseudo, pseudo, "", argument ];
return function( elem ) {
return fn( elem, 0, args );
};
}
return fn;
}
return fn( argument, context, xml );
}
},
pseudos: {
"not": markFunction(function( selector, context, xml ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var matcher = compile( selector.replace( rtrim, "$1" ), context, xml );
return function( elem ) {
return !matcher( elem );
};
}),
"enabled": function( elem ) {
return elem.disabled === false;
},
"disabled": function( elem ) {
return elem.disabled === true;
},
"checked": function( elem ) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
var nodeName = elem.nodeName.toLowerCase();
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
},
"selected": function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
"parent": function( elem ) {
return !Expr.pseudos["empty"]( elem );
},
"empty": function( elem ) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
// not comment, processing instructions, or others
// Thanks to Diego Perini for the nodeName shortcut
// Greater than "@" means alpha characters (specifically not starting with "#" or "?")
var nodeType;
elem = elem.firstChild;
while ( elem ) {
if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
return false;
}
elem = elem.nextSibling;
}
return true;
},
"contains": markFunction(function( text ) {
return function( elem ) {
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
};
}),
"has": markFunction(function( selector ) {
return function( elem ) {
return Sizzle( selector, elem ).length > 0;
};
}),
"header": function( elem ) {
return rheader.test( elem.nodeName );
},
"text": function( elem ) {
var type, attr;
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
// use getAttribute instead to test this case
return elem.nodeName.toLowerCase() === "input" &&
(type = elem.type) === "text" &&
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
},
// Input types
"radio": createInputPseudo("radio"),
"checkbox": createInputPseudo("checkbox"),
"file": createInputPseudo("file"),
"password": createInputPseudo("password"),
"image": createInputPseudo("image"),
"submit": createButtonPseudo("submit"),
"reset": createButtonPseudo("reset"),
"button": function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
},
"input": function( elem ) {
return rinputs.test( elem.nodeName );
},
"focus": function( elem ) {
var doc = elem.ownerDocument;
return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href);
},
"active": function( elem ) {
return elem === elem.ownerDocument.activeElement;
}
},
setFilters: {
"first": function( elements, argument, not ) {
return not ? elements.slice( 1 ) : [ elements[0] ];
},
"last": function( elements, argument, not ) {
var elem = elements.pop();
return not ? elements : [ elem ];
},
"even": function( elements, argument, not ) {
var results = [],
i = not ? 1 : 0,
len = elements.length;
for ( ; i < len; i = i + 2 ) {
results.push( elements[i] );
}
return results;
},
"odd": function( elements, argument, not ) {
var results = [],
i = not ? 0 : 1,
len = elements.length;
for ( ; i < len; i = i + 2 ) {
results.push( elements[i] );
}
return results;
},
"lt": function( elements, argument, not ) {
return not ? elements.slice( +argument ) : elements.slice( 0, +argument );
},
"gt": function( elements, argument, not ) {
return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 );
},
"eq": function( elements, argument, not ) {
var elem = elements.splice( +argument, 1 );
return not ? elements : elem;
}
}
};
function siblingCheck( a, b, ret ) {
if ( a === b ) {
return ret;
}
var cur = a.nextSibling;
while ( cur ) {
if ( cur === b ) {
return -1;
}
cur = cur.nextSibling;
}
return 1;
}
sortOrder = docElem.compareDocumentPosition ?
function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
return 0;
}
return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
a.compareDocumentPosition :
a.compareDocumentPosition(b) & 4
) ? -1 : 1;
} :
function( a, b ) {
// The nodes are identical, we can exit early
if ( a === b ) {
hasDuplicate = true;
return 0;
// Fallback to using sourceIndex (in IE) if it's available on both nodes
} else if ( a.sourceIndex && b.sourceIndex ) {
return a.sourceIndex - b.sourceIndex;
}
var al, bl,
ap = [],
bp = [],
aup = a.parentNode,
bup = b.parentNode,
cur = aup;
// If the nodes are siblings (or identical) we can do a quick check
if ( aup === bup ) {
return siblingCheck( a, b );
// If no parents were found then the nodes are disconnected
} else if ( !aup ) {
return -1;
} else if ( !bup ) {
return 1;
}
// Otherwise they're somewhere else in the tree so we need
// to build up a full list of the parentNodes for comparison
while ( cur ) {
ap.unshift( cur );
cur = cur.parentNode;
}
cur = bup;
while ( cur ) {
bp.unshift( cur );
cur = cur.parentNode;
}
al = ap.length;
bl = bp.length;
// Start walking down the tree looking for a discrepancy
for ( var i = 0; i < al && i < bl; i++ ) {
if ( ap[i] !== bp[i] ) {
return siblingCheck( ap[i], bp[i] );
}
}
// We ended someplace up the tree so do a sibling check
return i === al ?
siblingCheck( a, bp[i], -1 ) :
siblingCheck( ap[i], b, 1 );
};
// Always assume the presence of duplicates if sort doesn't
// pass them to our comparison function (as in Google Chrome).
[0, 0].sort( sortOrder );
baseHasDuplicate = !hasDuplicate;
// Document sorting and removing duplicates
Sizzle.uniqueSort = function( results ) {
var elem,
i = 1;
hasDuplicate = baseHasDuplicate;
results.sort( sortOrder );
if ( hasDuplicate ) {
for ( ; (elem = results[i]); i++ ) {
if ( elem === results[ i - 1 ] ) {
results.splice( i--, 1 );
}
}
}
return results;
};
Sizzle.error = function( msg ) {
throw new Error( "Syntax error, unrecognized expression: " + msg );
};
function tokenize( selector, context, xml, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, group, i,
preFilters, filters,
checkContext = !xml && context !== document,
// Token cache should maintain spaces
key = ( checkContext ? "" : "" ) + selector.replace( rtrim, "$1" ),
cached = tokenCache[ expando ][ key ];
if ( cached ) {
return parseOnly ? 0 : slice.call( cached, 0 );
}
soFar = selector;
groups = [];
i = 0;
preFilters = Expr.preFilter;
filters = Expr.filter;
while ( soFar ) {
// Comma and first run
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
soFar = soFar.slice( match[0].length );
tokens.selector = group;
}
groups.push( tokens = [] );
group = "";
// Need to make sure we're within a narrower context if necessary
// Adding a descendant combinator will generate what is needed
if ( checkContext ) {
soFar = " " + soFar;
}
}
matched = false;
// Combinators
if ( (match = rcombinators.exec( soFar )) ) {
group += match[0];
soFar = soFar.slice( match[0].length );
// Cast descendant combinators to space
matched = tokens.push({
part: match.pop().replace( rtrim, " " ),
string: match[0],
captures: match
});
}
// Filters
for ( type in filters ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
( match = preFilters[ type ](match, context, xml) )) ) {
group += match[0];
soFar = soFar.slice( match[0].length );
matched = tokens.push({
part: type,
string: match.shift(),
captures: match
});
}
}
if ( !matched ) {
break;
}
}
// Attach the full group as a selector
if ( group ) {
tokens.selector = group;
}
// Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
slice.call( tokenCache(key, groups), 0 );
}
function addCombinator( matcher, combinator, context, xml ) {
var dir = combinator.dir,
doneName = done++;
if ( !matcher ) {
// If there is no matcher to check, check against the context
matcher = function( elem ) {
return elem === context;
};
}
return combinator.first ?
function( elem ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 ) {
return matcher( elem ) && elem;
}
}
} :
xml ?
function( elem ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 ) {
if ( matcher( elem ) ) {
return elem;
}
}
}
} :
function( elem ) {
var cache,
dirkey = doneName + "." + dirruns,
cachedkey = dirkey + "." + cachedruns;
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 ) {
if ( (cache = elem[ expando ]) === cachedkey ) {
return elem.sizset;
} else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
if ( elem.sizset ) {
return elem;
}
} else {
elem[ expando ] = cachedkey;
if ( matcher( elem ) ) {
elem.sizset = true;
return elem;
}
elem.sizset = false;
}
}
}
};
}
function addMatcher( higher, deeper ) {
return higher ?
function( elem ) {
var result = deeper( elem );
return result && higher( result === true ? elem : result );
} :
deeper;
}
// ["TAG", ">", "ID", " ", "CLASS"]
function matcherFromTokens( tokens, context, xml ) {
var token, matcher,
i = 0;
for ( ; (token = tokens[i]); i++ ) {
if ( Expr.relative[ token.part ] ) {
matcher = addCombinator( matcher, Expr.relative[ token.part ], context, xml );
} else {
matcher = addMatcher( matcher, Expr.filter[ token.part ].apply(null, token.captures.concat( context, xml )) );
}
}
return matcher;
}
function matcherFromGroupMatchers( matchers ) {
return function( elem ) {
var matcher,
j = 0;
for ( ; (matcher = matchers[j]); j++ ) {
if ( matcher(elem) ) {
return true;
}
}
return false;
};
}
compile = Sizzle.compile = function( selector, context, xml ) {
var group, i, len,
cached = compilerCache[ expando ][ selector ];
// Return a cached group function if already generated (context dependent)
if ( cached && cached.context === context ) {
return cached;
}
// Generate a function of recursive functions that can be used to check each element
group = tokenize( selector, context, xml );
for ( i = 0, len = group.length; i < len; i++ ) {
group[i] = matcherFromTokens(group[i], context, xml);
}
// Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers(group) );
cached.context = context;
cached.runs = cached.dirruns = 0;
return cached;
};
function multipleContexts( selector, contexts, results, seed ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[i], results, seed );
}
}
function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) {
var results,
fn = Expr.setFilters[ posfilter.toLowerCase() ];
if ( !fn ) {
Sizzle.error( posfilter );
}
if ( selector || !(results = seed) ) {
multipleContexts( selector || "*", contexts, (results = []), seed );
}
return results.length > 0 ? fn( results, argument, not ) : [];
}
function handlePOS( groups, context, results, seed ) {
var group, part, j, groupLen, token, selector,
anchor, elements, match, matched,
lastIndex, currentContexts, not,
i = 0,
len = groups.length,
rpos = matchExpr["POS"],
// This is generated here in case matchExpr["POS"] is extended
rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ),
// This is for making sure non-participating
// matching groups are represented cross-browser (IE6-8)
setUndefined = function() {
var i = 1,
len = arguments.length - 2;
for ( ; i < len; i++ ) {
if ( arguments[i] === undefined ) {
match[i] = undefined;
}
}
};
for ( ; i < len; i++ ) {
group = groups[i];
part = "";
elements = seed;
for ( j = 0, groupLen = group.length; j < groupLen; j++ ) {
token = group[j];
selector = token.string;
if ( token.part === "PSEUDO" ) {
// Reset regex index to 0
rpos.exec("");
anchor = 0;
while ( (match = rpos.exec( selector )) ) {
matched = true;
lastIndex = rpos.lastIndex = match.index + match[0].length;
if ( lastIndex > anchor ) {
part += selector.slice( anchor, match.index );
anchor = lastIndex;
currentContexts = [ context ];
if ( rcombinators.test(part) ) {
if ( elements ) {
currentContexts = elements;
}
elements = seed;
}
if ( (not = rendsWithNot.test( part )) ) {
part = part.slice( 0, -5 ).replace( rcombinators, "$&*" );
anchor++;
}
if ( match.length > 1 ) {
match[0].replace( rposgroups, setUndefined );
}
elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not );
}
part = "";
}
}
if ( !matched ) {
part += selector;
}
matched = false;
}
if ( part ) {
if ( rcombinators.test(part) ) {
multipleContexts( part, elements || [ context ], results, seed );
} else {
Sizzle( part, context, results, seed ? seed.concat(elements) : elements );
}
} else {
push.apply( results, elements );
}
}
// Do not sort if this is a single filter
return len === 1 ? results : Sizzle.uniqueSort( results );
}
function select( selector, context, results, seed, xml ) {
// Remove excessive whitespace
selector = selector.replace( rtrim, "$1" );
var elements, matcher, cached, elem,
i, tokens, token, lastToken, findContext, type,
match = tokenize( selector, context, xml ),
contextNodeType = context.nodeType;
// POS handling
if ( matchExpr["POS"].test(selector) ) {
return handlePOS( match, context, results, seed );
}
if ( seed ) {
elements = slice.call( seed, 0 );
// To maintain document order, only narrow the
// set if there is one group
} else if ( match.length === 1 ) {
// Take a shortcut and set the context if the root selector is an ID
if ( (tokens = slice.call( match[0], 0 )).length > 2 &&
(token = tokens[0]).part === "ID" &&
contextNodeType === 9 && !xml &&
Expr.relative[ tokens[1].part ] ) {
context = Expr.find["ID"]( token.captures[0].replace( rbackslash, "" ), context, xml )[0];
if ( !context ) {
return results;
}
selector = selector.slice( tokens.shift().string.length );
}
findContext = ( (match = rsibling.exec( tokens[0].string )) && !match.index && context.parentNode ) || context;
// Reduce the set if possible
lastToken = "";
for ( i = tokens.length - 1; i >= 0; i-- ) {
token = tokens[i];
type = token.part;
lastToken = token.string + lastToken;
if ( Expr.relative[ type ] ) {
break;
}
if ( Expr.order.test(type) ) {
elements = Expr.find[ type ]( token.captures[0].replace( rbackslash, "" ), findContext, xml );
if ( elements == null ) {
continue;
} else {
selector = selector.slice( 0, selector.length - lastToken.length ) +
lastToken.replace( matchExpr[ type ], "" );
if ( !selector ) {
push.apply( results, slice.call(elements, 0) );
}
break;
}
}
}
}
// Only loop over the given elements once
if ( selector ) {
matcher = compile( selector, context, xml );
dirruns = matcher.dirruns++;
if ( elements == null ) {
elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context );
}
for ( i = 0; (elem = elements[i]); i++ ) {
cachedruns = matcher.runs++;
if ( matcher(elem) ) {
results.push( elem );
}
}
}
return results;
}
if ( document.querySelectorAll ) {
(function() {
var disconnectedMatch,
oldSelect = select,
rescape = /'|\\/g,
rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
rbuggyQSA = [],
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
// A support test would require too much code (would include document ready)
// just skip matchesSelector for :active
rbuggyMatches = [":active"],
matches = docElem.matchesSelector ||
docElem.mozMatchesSelector ||
docElem.webkitMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector;
// Build QSA regex
// Regex strategy adopted from Diego Perini
assert(function( div ) {
// Select is set to empty string on purpose
// This is to test IE's treatment of not explictly
// setting a boolean content attribute,
// since its presence should be enough
// http://bugs.jquery.com/ticket/12359
div.innerHTML = "";
// IE8 - Some boolean attributes are not treated correctly
if ( !div.querySelectorAll("[selected]").length ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
}
// Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here (do not put tests after this one)
if ( !div.querySelectorAll(":checked").length ) {
rbuggyQSA.push(":checked");
}
});
assert(function( div ) {
// Opera 10-12/IE9 - ^= $= *= and empty values
// Should not select anything
div.innerHTML = "";
if ( div.querySelectorAll("[test^='']").length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
}
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
// IE8 throws error here (do not put tests after this one)
div.innerHTML = "";
if ( !div.querySelectorAll(":enabled").length ) {
rbuggyQSA.push(":enabled", ":disabled");
}
});
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
select = function( selector, context, results, seed, xml ) {
// Only use querySelectorAll when not filtering,
// when this is not xml,
// and when no QSA bugs apply
if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
if ( context.nodeType === 9 ) {
try {
push.apply( results, slice.call(context.querySelectorAll( selector ), 0) );
return results;
} catch(qsaError) {}
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
var groups, i, len,
old = context.getAttribute("id"),
nid = old || expando,
newContext = rsibling.test( selector ) && context.parentNode || context;
if ( old ) {
nid = nid.replace( rescape, "\\$&" );
} else {
context.setAttribute( "id", nid );
}
groups = tokenize(selector, context, xml);
// Trailing space is unnecessary
// There is always a context check
nid = "[id='" + nid + "']";
for ( i = 0, len = groups.length; i < len; i++ ) {
groups[i] = nid + groups[i].selector;
}
try {
push.apply( results, slice.call( newContext.querySelectorAll(
groups.join(",")
), 0 ) );
return results;
} catch(qsaError) {
} finally {
if ( !old ) {
context.removeAttribute("id");
}
}
}
}
return oldSelect( selector, context, results, seed, xml );
};
if ( matches ) {
assert(function( div ) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9)
disconnectedMatch = matches.call( div, "div" );
// This should fail with an exception
// Gecko does not error, returns false instead
try {
matches.call( div, "[test!='']:sizzle" );
rbuggyMatches.push( matchExpr["PSEUDO"].source, matchExpr["POS"].source, "!=" );
} catch ( e ) {}
});
// rbuggyMatches always contains :active, so no need for a length check
rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
Sizzle.matchesSelector = function( elem, expr ) {
// Make sure that attribute selectors are quoted
expr = expr.replace( rattributeQuotes, "='$1']" );
// rbuggyMatches always contains :active, so no need for an existence check
if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) {
try {
var ret = matches.call( elem, expr );
// IE 9's matchesSelector returns false on disconnected nodes
if ( ret || disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9
elem.document && elem.document.nodeType !== 11 ) {
return ret;
}
} catch(e) {}
}
return Sizzle( expr, null, null, [ elem ] ).length > 0;
};
}
})();
}
// Deprecated
Expr.setFilters["nth"] = Expr.setFilters["eq"];
// Back-compat
Expr.filters = Expr.pseudos;
// Override sizzle attribute retrieval
Sizzle.attr = jQuery.attr;
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
})( window );
var runtil = /Until$/,
rparentsprev = /^(?:parents|prev(?:Until|All))/,
isSimple = /^.[^:#\[\.,]*$/,
rneedsContext = jQuery.expr.match.needsContext,
// methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
};
jQuery.fn.extend({
find: function( selector ) {
var i, l, length, n, r, ret,
self = this;
if ( typeof selector !== "string" ) {
return jQuery( selector ).filter(function() {
for ( i = 0, l = self.length; i < l; i++ ) {
if ( jQuery.contains( self[ i ], this ) ) {
return true;
}
}
});
}
ret = this.pushStack( "", "find", selector );
for ( i = 0, l = this.length; i < l; i++ ) {
length = ret.length;
jQuery.find( selector, this[i], ret );
if ( i > 0 ) {
// Make sure that the results are unique
for ( n = length; n < ret.length; n++ ) {
for ( r = 0; r < length; r++ ) {
if ( ret[r] === ret[n] ) {
ret.splice(n--, 1);
break;
}
}
}
}
}
return ret;
},
has: function( target ) {
var i,
targets = jQuery( target, this ),
len = targets.length;
return this.filter(function() {
for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) {
return true;
}
}
});
},
not: function( selector ) {
return this.pushStack( winnow(this, selector, false), "not", selector);
},
filter: function( selector ) {
return this.pushStack( winnow(this, selector, true), "filter", selector );
},
is: function( selector ) {
return !!selector && (
typeof selector === "string" ?
// If this is a positional/relative selector, check membership in the returned set
// so $("p:first").is("p:last") won't return true for a doc with two "p".
rneedsContext.test( selector ) ?
jQuery( selector, this.context ).index( this[0] ) >= 0 :
jQuery.filter( selector, this ).length > 0 :
this.filter( selector ).length > 0 );
},
closest: function( selectors, context ) {
var cur,
i = 0,
l = this.length,
ret = [],
pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
jQuery( selectors, context || this.context ) :
0;
for ( ; i < l; i++ ) {
cur = this[i];
while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
ret.push( cur );
break;
}
cur = cur.parentNode;
}
}
ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
return this.pushStack( ret, "closest", selectors );
},
// Determine the position of an element within
// the matched set of elements
index: function( elem ) {
// No argument, return index in parent
if ( !elem ) {
return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
}
// index in selector
if ( typeof elem === "string" ) {
return jQuery.inArray( this[0], jQuery( elem ) );
}
// Locate the position of the desired element
return jQuery.inArray(
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[0] : elem, this );
},
add: function( selector, context ) {
var set = typeof selector === "string" ?
jQuery( selector, context ) :
jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
all = jQuery.merge( this.get(), set );
return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
all :
jQuery.unique( all ) );
},
addBack: function( selector ) {
return this.add( selector == null ?
this.prevObject : this.prevObject.filter(selector)
);
}
});
jQuery.fn.andSelf = jQuery.fn.addBack;
// A painfully simple check to see if an element is disconnected
// from a document (should be improved, where feasible).
function isDisconnected( node ) {
return !node || !node.parentNode || node.parentNode.nodeType === 11;
}
function sibling( cur, dir ) {
do {
cur = cur[ dir ];
} while ( cur && cur.nodeType !== 1 );
return cur;
}
jQuery.each({
parent: function( elem ) {
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function( elem ) {
return jQuery.dir( elem, "parentNode" );
},
parentsUntil: function( elem, i, until ) {
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
return sibling( elem, "nextSibling" );
},
prev: function( elem ) {
return sibling( elem, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
},
prevAll: function( elem ) {
return jQuery.dir( elem, "previousSibling" );
},
nextUntil: function( elem, i, until ) {
return jQuery.dir( elem, "nextSibling", until );
},
prevUntil: function( elem, i, until ) {
return jQuery.dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
},
children: function( elem ) {
return jQuery.sibling( elem.firstChild );
},
contents: function( elem ) {
return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document :
jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until );
if ( !runtil.test( name ) ) {
selector = until;
}
if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret );
}
ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
if ( this.length > 1 && rparentsprev.test( name ) ) {
ret = ret.reverse();
}
return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
};
});
jQuery.extend({
filter: function( expr, elems, not ) {
if ( not ) {
expr = ":not(" + expr + ")";
}
return elems.length === 1 ?
jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
jQuery.find.matches(expr, elems);
},
dir: function( elem, dir, until ) {
var matched = [],
cur = elem[ dir ];
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) {
matched.push( cur );
}
cur = cur[dir];
}
return matched;
},
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
});
// Implement the identical functionality for filter and not
function winnow( elements, qualifier, keep ) {
// Can't pass null or undefined to indexOf in Firefox 4
// Set to 0 to skip string check
qualifier = qualifier || 0;
if ( jQuery.isFunction( qualifier ) ) {
return jQuery.grep(elements, function( elem, i ) {
var retVal = !!qualifier.call( elem, i, elem );
return retVal === keep;
});
} else if ( qualifier.nodeType ) {
return jQuery.grep(elements, function( elem, i ) {
return ( elem === qualifier ) === keep;
});
} else if ( typeof qualifier === "string" ) {
var filtered = jQuery.grep(elements, function( elem ) {
return elem.nodeType === 1;
});
if ( isSimple.test( qualifier ) ) {
return jQuery.filter(qualifier, filtered, !keep);
} else {
qualifier = jQuery.filter( qualifier, filtered );
}
}
return jQuery.grep(elements, function( elem, i ) {
return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
});
}
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();
if ( safeFrag.createElement ) {
while ( list.length ) {
safeFrag.createElement(
list.pop()
);
}
}
return safeFrag;
}
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
rleadingWhitespace = /^\s+/,
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagName = /<([\w:]+)/,
rtbody = /]", "i"),
rcheckableType = /^(?:checkbox|radio)$/,
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /\/(java|ecma)script/i,
rcleanScript = /^\s*\s*$/g,
wrapMap = {
option: [ 1, "" ],
legend: [ 1, "" ],
thead: [ 1, "
", "
" ],
tr: [ 2, "
", "
" ],
td: [ 3, "
", "
" ],
col: [ 2, "
", "
" ],
area: [ 1, "" ],
_default: [ 0, "", "" ]
},
safeFragment = createSafeFragment( document ),
fragmentDiv = safeFragment.appendChild( document.createElement("div") );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
// unless wrapped in a div with non-breaking characters in front of it.
if ( !jQuery.support.htmlSerialize ) {
wrapMap._default = [ 1, "X
", "
" ];
}
jQuery.fn.extend({
text: function( value ) {
return jQuery.access( this, function( value ) {
return value === undefined ?
jQuery.text( this ) :
this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
}, null, value, arguments.length );
},
wrapAll: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapAll( html.call(this, i) );
});
}
if ( this[0] ) {
// The elements to wrap the target around
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
if ( this[0].parentNode ) {
wrap.insertBefore( this[0] );
}
wrap.map(function() {
var elem = this;
while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
elem = elem.firstChild;
}
return elem;
}).append( this );
}
return this;
},
wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapInner( html.call(this, i) );
});
}
return this.each(function() {
var self = jQuery( this ),
contents = self.contents();
if ( contents.length ) {
contents.wrapAll( html );
} else {
self.append( html );
}
});
},
wrap: function( html ) {
var isFunction = jQuery.isFunction( html );
return this.each(function(i) {
jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
});
},
unwrap: function() {
return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) {
jQuery( this ).replaceWith( this.childNodes );
}
}).end();
},
append: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 ) {
this.appendChild( elem );
}
});
},
prepend: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 ) {
this.insertBefore( elem, this.firstChild );
}
});
},
before: function() {
if ( !isDisconnected( this[0] ) ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this );
});
}
if ( arguments.length ) {
var set = jQuery.clean( arguments );
return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
}
},
after: function() {
if ( !isDisconnected( this[0] ) ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this.nextSibling );
});
}
if ( arguments.length ) {
var set = jQuery.clean( arguments );
return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
}
},
// keepData is for internal use only--do not document
remove: function( selector, keepData ) {
var elem,
i = 0;
for ( ; (elem = this[i]) != null; i++ ) {
if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
if ( !keepData && elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
jQuery.cleanData( [ elem ] );
}
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
}
return this;
},
empty: function() {
var elem,
i = 0;
for ( ; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
}
// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
}
return this;
},
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
html: function( value ) {
return jQuery.access( this, function( value ) {
var elem = this[0] || {},
i = 0,
l = this.length;
if ( value === undefined ) {
return elem.nodeType === 1 ?
elem.innerHTML.replace( rinlinejQuery, "" ) :
undefined;
}
// See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
!wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
value = value.replace( rxhtmlTag, "<$1>$2>" );
try {
for (; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
elem = this[i] || {};
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName( "*" ) );
elem.innerHTML = value;
}
}
elem = 0;
// If using innerHTML throws an exception, use the fallback method
} catch(e) {}
}
if ( elem ) {
this.empty().append( value );
}
}, null, value, arguments.length );
},
replaceWith: function( value ) {
if ( !isDisconnected( this[0] ) ) {
// Make sure that the elements are removed from the DOM before they are inserted
// this can help fix replacing a parent with child elements
if ( jQuery.isFunction( value ) ) {
return this.each(function(i) {
var self = jQuery(this), old = self.html();
self.replaceWith( value.call( this, i, old ) );
});
}
if ( typeof value !== "string" ) {
value = jQuery( value ).detach();
}
return this.each(function() {
var next = this.nextSibling,
parent = this.parentNode;
jQuery( this ).remove();
if ( next ) {
jQuery(next).before( value );
} else {
jQuery(parent).append( value );
}
});
}
return this.length ?
this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
this;
},
detach: function( selector ) {
return this.remove( selector, true );
},
domManip: function( args, table, callback ) {
// Flatten any nested arrays
args = [].concat.apply( [], args );
var results, first, fragment, iNoClone,
i = 0,
value = args[0],
scripts = [],
l = this.length;
// We can't cloneNode fragments that contain checked, in WebKit
if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
return this.each(function() {
jQuery(this).domManip( args, table, callback );
});
}
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
args[0] = value.call( this, i, table ? self.html() : undefined );
self.domManip( args, table, callback );
});
}
if ( this[0] ) {
results = jQuery.buildFragment( args, this, scripts );
fragment = results.fragment;
first = fragment.firstChild;
if ( fragment.childNodes.length === 1 ) {
fragment = first;
}
if ( first ) {
table = table && jQuery.nodeName( first, "tr" );
// Use the original fragment for the last item instead of the first because it can end up
// being emptied incorrectly in certain situations (#8070).
// Fragments from the fragment cache must always be cloned and never used in place.
for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
callback.call(
table && jQuery.nodeName( this[i], "table" ) ?
findOrAppend( this[i], "tbody" ) :
this[i],
i === iNoClone ?
fragment :
jQuery.clone( fragment, true, true )
);
}
}
// Fix #11809: Avoid leaking memory
fragment = first = null;
if ( scripts.length ) {
jQuery.each( scripts, function( i, elem ) {
if ( elem.src ) {
if ( jQuery.ajax ) {
jQuery.ajax({
url: elem.src,
type: "GET",
dataType: "script",
async: false,
global: false,
"throws": true
});
} else {
jQuery.error("no ajax");
}
} else {
jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
}
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
});
}
}
return this;
}
});
function findOrAppend( elem, tag ) {
return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
}
function cloneCopyEvent( src, dest ) {
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
return;
}
var type, i, l,
oldData = jQuery._data( src ),
curData = jQuery._data( dest, oldData ),
events = oldData.events;
if ( events ) {
delete curData.handle;
curData.events = {};
for ( type in events ) {
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( dest, type, events[ type ][ i ] );
}
}
}
// make the cloned public data object a copy from the original
if ( curData.data ) {
curData.data = jQuery.extend( {}, curData.data );
}
}
function cloneFixAttributes( src, dest ) {
var nodeName;
// We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) {
return;
}
// clearAttributes removes the attributes, which we don't want,
// but also removes the attachEvent events, which we *do* want
if ( dest.clearAttributes ) {
dest.clearAttributes();
}
// mergeAttributes, in contrast, only merges back on the
// original attributes, not the events
if ( dest.mergeAttributes ) {
dest.mergeAttributes( src );
}
nodeName = dest.nodeName.toLowerCase();
if ( nodeName === "object" ) {
// IE6-10 improperly clones children of object elements using classid.
// IE10 throws NoModificationAllowedError if parent is null, #12132.
if ( dest.parentNode ) {
dest.outerHTML = src.outerHTML;
}
// This path appears unavoidable for IE9. When cloning an object
// element in IE9, the outerHTML strategy above is not sufficient.
// If the src has innerHTML and the destination does not,
// copy the src.innerHTML into the dest.innerHTML. #10324
if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
dest.innerHTML = src.innerHTML;
}
} else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
// IE6-8 fails to persist the checked state of a cloned checkbox
// or radio button. Worse, IE6-7 fail to give the cloned element
// a checked appearance if the defaultChecked value isn't also set
dest.defaultChecked = dest.checked = src.checked;
// IE6-7 get confused and end up setting the value of a cloned
// checkbox/radio button to an empty string instead of "on"
if ( dest.value !== src.value ) {
dest.value = src.value;
}
// IE6-8 fails to return the selected option to the default selected
// state when cloning options
} else if ( nodeName === "option" ) {
dest.selected = src.defaultSelected;
// IE6-8 fails to set the defaultValue to the correct value when
// cloning other types of input fields
} else if ( nodeName === "input" || nodeName === "textarea" ) {
dest.defaultValue = src.defaultValue;
// IE blanks contents when cloning scripts
} else if ( nodeName === "script" && dest.text !== src.text ) {
dest.text = src.text;
}
// Event data gets referenced instead of copied if the expando
// gets copied too
dest.removeAttribute( jQuery.expando );
}
jQuery.buildFragment = function( args, context, scripts ) {
var fragment, cacheable, cachehit,
first = args[ 0 ];
// Set context from what may come in as undefined or a jQuery collection or a node
// Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 &
// also doubles as fix for #8950 where plain objects caused createDocumentFragment exception
context = context || document;
context = !context.nodeType && context[0] || context;
context = context.ownerDocument || context;
// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put