Repository: imakewebthings/waypoints
Branch: master
Commit: 34d9f6d77016
Files: 48
Total size: 522.7 KB
Directory structure:
gitextract___33l_r6/
├── .eslintrc
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── README.md
├── bower.json
├── gulpfile.js
├── lib/
│ ├── jquery.waypoints.js
│ ├── noframework.waypoints.js
│ ├── shortcuts/
│ │ ├── infinite.js
│ │ ├── inview.js
│ │ └── sticky.js
│ ├── waypoints.debug.js
│ └── zepto.waypoints.js
├── licenses.txt
├── package.json
├── src/
│ ├── adapters/
│ │ ├── jquery-zepto-fn-extension.js
│ │ ├── jquery.js
│ │ ├── noframework.js
│ │ └── zepto.js
│ ├── context.js
│ ├── debug.js
│ ├── group.js
│ ├── shortcuts/
│ │ ├── infinite.js
│ │ ├── inview.js
│ │ └── sticky.js
│ └── waypoint.js
├── test/
│ ├── adapter-fn-spec.js
│ ├── context-spec.js
│ ├── debug-spec.js
│ ├── fixtures/
│ │ ├── infinite.html
│ │ ├── standard.html
│ │ └── sticky.html
│ ├── group-spec.js
│ ├── infinite-spec.js
│ ├── inview-spec.js
│ ├── lib/
│ │ ├── coffee-script.js
│ │ ├── jasmine-html.js
│ │ ├── jasmine-jquery.js
│ │ ├── jasmine.css
│ │ ├── jasmine.js
│ │ ├── require.js
│ │ └── testloader.js
│ ├── settings.js
│ ├── sticky-spec.js
│ └── waypoint-spec.js
└── testem.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc
================================================
{
"env": {
"browser": true,
"node": true
},
"rules": {
"brace-style": [1, "stroustrup"],
"consistent-this": [1, "self"],
"eqeqeq": [1, "smart"],
"func-style": [1, "declaration"],
"no-else-return": 1,
"no-extra-parens": 1,
"no-floating-decimal": 1,
"no-nested-ternary": 1,
"no-lonely-if": 1,
"quotes": [1, "single", "avoid-escape"],
"radix": 1,
"semi": [1, "never"],
"space-after-keywords": [1, "always"],
"space-in-brackets": [1, "never"],
"space-unary-word-ops": 1,
"wrap-iife": 1
}
}
================================================
FILE: .gitignore
================================================
_site
.sass-cache
.DS_Store
/style.scss
*.gz
node_modules
bower_components
================================================
FILE: .travis.yml
================================================
language: node_js
sudo: false
node_js:
- "0.10"
before_script:
- npm install -g bower
- bower install
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## v4.0.1
- Improve performance of `enableAll`. (Issue #454)
- Handle edge case bug where Waypoint initialization during a specific part of iOS scroll bounce would cause an immediate trigger of it. (Issue #499)
- Maintain `window` Context/resize-handler even when there are only non-window-context waypoints. (Issue #442)
## v4.0.0
- Allow Sticky option `wrapper` to accept false, which will not create a wrapper and instead use the preexisting parent element. (Pull #416)
- Waypoints that are immediately triggered on creation because they've already passed their trigger point now run their handlers on the next animation frame. This contains Zalgo. (Issue #384)
- Pass the jQuery object of items added during an Infinite page load to the `onAfterPageLoad` callback. (Pull #398)
- Add `enabled` option, `enable` and `disable` methods to the Inview shortcut (Pull #406)
- Make the Inview instance `this` within the callbacks, rather than the invdividual underlying waypoints. (Issue #412)
- Account for changes to jQuery 3 around calling `offset` on the window. (Pull #430)
- Add `context` option to Inview. (Issue #433)
## v3.1.1
- Fix bad `isWindow` checks causing errors in IE8-. (Issue #372)
## v3.1.0
- Add `Waypoint.disableAll` and `Waypoint.enableAll` methods.
- Fix Illegal Invocation errors stemming from non-window context use of `requestAnimationFrame`. (Pull #366)
- Keep disabled waypoints from triggering debug script errors. (Pull #365)
- Allow Infinite Scroll items to be root elements in the AJAX response. (Pull #361)
- In debug script, detect display none and fixed positioning defined in CSS.
## v3.0.1
- Add semicolons to the end of built files to aid in clean concatenation. (Issue #353)
## v3.0.0
- Remove hard jQuery dependency. Create builds for jQuery, Zepto, and no DOM framework. (Issue #282)
- Expose `Waypoint` and `Context` classes. (Issue #281)
- Add `Group` class and `group` option for grouping waypoints. Make `continuous` option work within these groups. (Issue #264)
- Add Inview shortcut. (Issue #131)
- Extend `continuous` option to cover refreshes. (Issue #166)
- Throttle resize and scroll handlers using `requestAnimationFrame` instead of a set millisecond timeout. Fallback to the old 60 FPS `setTimeout` throttle for unsupported browsers. (Issue #242)
- Add debugging script for diagnosing common problems.
- Remove `triggerOnce` option.
- Add `viewportWidth` utility method.
- Remove all traces of CoffeeScript.
## v2.0.5
- Allow sticky users to define which direction the stuck class shold be applied. (Issue #192)
- Fix bug where short content on a large screen could cause the infinite shortcut to stall after the first page load. (Issue #207)
- Make `unsticky` safe to use on any element. Previously it would unwrap the parent even if the element had never had `sticky` called on it or already had `unsticky` called previously. (Issue #225)
- Fix bug that would cause handlers to be overwritten when trying to reuse an options object. (Issue #253)
- Remove "More" link when infinite shortcut reaches last page. (Issue #260)
- Fix use of `this` instead of `window`, causing Browserify to fail. (Issue #262)
- Stop using deprecated jQuery `load` method. (Issue #283)
## v2.0.4
- Fix enable, disable, and destroys calls not chaining the jQuery object. (Issue #244) (Thanks [@robharper](https://github.com/robharper))
- Fix destroy not unregistering internal waypoint references if underlying node has been removed from the document, causing memory leaks. (Issue #243)
## v2.0.3
- Add "unsticky" function for sticky shortcut. (Issue #130)
- Exit early from Infinite shortcut if no "more" link exists. (Issue #140)
- Delay height evaluation of sticky shortcut wrapper. (Issue #151)
- Fix errors with Infinite shortcut's parsing of HTML with jQuery 1.9+. (Issue #163)
## v2.0.2
- Add AMD support. (Issue #116)
- Work around iOS issue with cancelled `setTimeout` timers by not using scroll throttling on touch devices. (Issue #120)
- If defined, execute `handler` option passed to sticky shortcut at the end of the stuck/unstuck change. (Issue #123)
## v2.0.1
- Lower default throttle values for `scrollThrottle` and `resizeThrottle`.
- Fix Issue #104: Pixel offsets written as strings are interpreted as %s.
- Fix Issue #100: Work around IE not firing scroll event on document shortening by forcing a scroll check on `refresh` calls.
## v2.0.0
- Rewrite Waypoints in CoffeeScript.
- Add Sticky and Infinite shortcut scripts.
- Allow multiple Waypoints on each element. (Issue #40)
- Allow horizontal scrolling Waypoints. (Issue #14)
- API additions: (#69, 83, 88)
- prev, next, above, below, left, right, extendFn, enable, disable
- API subtractions:
- remove
- Remove custom 'waypoint.reached' jQuery Event from powering the trigger.
- $.waypoints now returns object with vertical+horizontal properties and HTMLElement arrays instead of jQuery object (to preserve trigger order instead of jQuery's forced source order).
- Add enabled option.
## v1.1.7
- Actually fix the post-load bug in Issue #28 from v1.1.3.
## v1.1.6
- Fix potential memory leak by unbinding events on empty context elements.
## v1.1.5
- Make plugin compatible with Browserify/RequireJS. (Thanks [@cjroebuck](https://github.com/cjroebuck))
## v1.1.4
- Add handler option to give alternate binding method.
## v1.1.3
- Fix cases where waypoints are added post-load and should be triggered immediately.
## v1.1.2
- Fixed error thrown by waypoints with triggerOnce option that were triggered via resize refresh.
## v1.1.1
- Fixed bug in initialization where all offsets were being calculated as if set to 0 initially, causing unwarranted triggers during the subsequent refresh.
- Added `onlyOnScroll`, an option for individual waypoints that disables triggers due to an offset refresh that crosses the current scroll point. (All credit to [@knuton](https://github.com/knuton) on this one.)
## v1.1
- Moved the continuous option out of global settings and into the options
object for individual waypoints.
- Added the context option, which allows for using waypoints within any
scrollable element, not just the window.
## v1.0.2
- Moved scroll and resize handler bindings out of load. Should play nicer with async loaders like Head JS and LABjs.
- Fixed a 1px off error when using certain % offsets.
- Added unit tests.
## v1.0.1
- Added $.waypoints('viewportHeight').
- Fixed iOS bug (using the new viewportHeight method).
- Added offset function alias: 'bottom-in-view'.
## v1.0
- Initial release.
================================================
FILE: CONTRIBUTING.md
================================================
# Opening an Issue
The GitHub issue tracker is exclusively for opening demonstrable bugs with the library or for discussing/implementing enhancements. If you need general help with Waypoints try searching through existing closed tickets, searching through the [#jquery-waypoints](http://stackoverflow.com/questions/tagged/jquery-waypoints) tag on StackOverflow, or asking your question there using that tag. If you do ask a question on StackOverflow, please follow the guidelines for [asking a good question](http://stackoverflow.com/help/how-to-ask).
If you're opening a ticket for a bug:
- Give a clear explanation of the bug.
- Try to provide a link to a [JSFiddle](http://jsfiddle.net/) or [CodePen](http://codepen.io/) or similar reduced test case.
- If you cannot provide a reduced test case, please provide a link to a live site demonstrating your bug and include in the ticket the relevant Waypoints code.
If you're interested in discussing a possible new feature:
- Search closed tickets for discussions that may have already occurred.
- Open a ticket and let's talk!
# Pull Requests
- Please send the pull request against the master branch.
- Note any tickets that the pull request addresses.
- Add any necessary tests (see below).
- Follow the coding style of the current codebase.
# Tests
Tests are written in [Jasmine](http://jasmine.github.io/) and run through the [testem](https://github.com/airportyh/testem) test runner. To run them locally you'll need to:
- Install, if you haven't already: [PhantomJS](http://phantomjs.org/), node, and [Bower](bower.io).
- `npm install`
- `bower install`
You can then run the tests one time by running `npm test`, or enter TDD mode by running `npm run tdd`.
================================================
FILE: README.md
================================================
# Waypoints
Waypoints is a library that makes it easy to execute a function whenever you scroll to an element. 
```js
var waypoint = new Waypoint({
element: document.getElementById('thing'),
handler: function(direction) {
alert('You have scrolled to a thing')
}
})
```
If you're new to Waypoints, check out the [Getting Started](http://imakewebthings.com/waypoints/guides/getting-started) guide.
[Read the full documentation](http://imakewebthings.com/waypoints/api/waypoint) for more details on usage and customization.
## Shortcuts
In addition to the normal Waypoints script, extensions exist to make common UI patterns just a little easier to implement:
- [Infinite Scrolling](http://imakewebthings.com/waypoints/shortcuts/infinite-scroll)
- [Sticky Elements](http://imakewebthings.com/waypoints/shortcuts/sticky-elements)
- [Inview Detection](http://imakewebthings.com/waypoints/shortcuts/inview)
## Contributing
If you want to report a Waypoints bug or contribute code to the library, please read the [Contributing Guidelines](https://github.com/imakewebthings/waypoints/blob/master/CONTRIBUTING.md). If you need help *using* Waypoints, please do not open an issue. Instead, ask the question on [Stack Overflow](http://stackoverflow.com) and tag it with #jquery-waypoints. Be sure to follow the guidelines for [asking a good question](http://stackoverflow.com/help/how-to-ask).
## License
Copyright (c) 2011-2014 Caleb Troughton. Licensed under the [MIT license](https://github.com/imakewebthings/waypoints/blob/master/licenses.txt).
================================================
FILE: bower.json
================================================
{
"name": "waypoints",
"main": "lib/noframework.waypoints.js",
"description": "Easily execute a function when you scroll to an element.",
"ignore": [
"gulpfile.js",
"package.json",
"src",
"test",
"testem.json"
],
"devDependencies": {
"jquery": "~1.11.1",
"lodash": "~2.4.1",
"jasmine-jquery": "~1.7.0",
"zepto": "~1.1.3"
}
}
================================================
FILE: gulpfile.js
================================================
var gulp = require('gulp')
var eslint = require('gulp-eslint')
var concat = require('gulp-concat')
var uglify = require('gulp-uglify')
var rename = require('gulp-rename')
var header = require('gulp-header')
var footer = require('gulp-footer')
var tap = require('gulp-tap')
var merge = require('merge-stream')
var pkg = require('./package.json')
var path = require('path')
var jsFiles = ['src/**/*.js', 'test/**/*.js', '!test/lib/**/*.js']
var shortcutTitles = {
'infinite': 'Waypoints Infinite Scroll Shortcut',
'inview': 'Waypoints Inview Shortcut',
'sticky': 'Waypoints Sticky Element Shortcut'
}
function fileHeader(title) {
return [
'/*!',
title + ' - ' + pkg.version,
'Copyright © 2011-' + new Date().getFullYear() + ' Caleb Troughton',
'Licensed under the MIT license.',
'https://github.com/imakewebthings/waypoints/blob/master/licenses.txt',
'*/\n'
].join('\n')
}
gulp.task('lint', function() {
return gulp.src(jsFiles).pipe(eslint('.eslintrc')).pipe(eslint.format())
})
gulp.task('build-core', function() {
var streams = ['noframework', 'jquery', 'zepto'].map(function(adapter) {
var sources = [
'src/waypoint.js',
'src/context.js',
'src/group.js',
'src/adapters/' + adapter + '.js'
]
if (['jquery', 'zepto'].indexOf(adapter) > -1) {
sources.push('src/adapters/jquery-zepto-fn-extension.js')
}
return gulp.src(sources)
.pipe(concat(adapter + '.waypoints.js', { newLine: ';' }))
.pipe(header(fileHeader('Waypoints')))
.pipe(footer(';'))
.pipe(gulp.dest('lib/'))
.pipe(rename(adapter + '.waypoints.min.js'))
.pipe(uglify({
preserveComments: 'some'
}))
.pipe(gulp.dest('lib/'))
})
return merge.apply(null, streams)
})
gulp.task('build-shortcuts', function() {
return gulp.src([
'src/shortcuts/*.js'
])
.pipe(tap(function(file) {
var title = path.basename(file.path, '.js')
file.contents = Buffer.concat([
new Buffer(fileHeader(shortcutTitles[title])),
file.contents
])
}))
.pipe(footer(';'))
.pipe(gulp.dest('lib/shortcuts/'))
.pipe(rename(function(path) {
path.basename += '.min'
}))
.pipe(uglify({
preserveComments: 'some'
}))
.pipe(gulp.dest('lib/shortcuts/'))
})
gulp.task('build-debug', function() {
return gulp.src([
'src/debug.js'
])
.pipe(rename('waypoints.debug.js'))
.pipe(header(fileHeader('Waypoints Debug')))
.pipe(footer(';'))
.pipe(gulp.dest('lib/'))
})
gulp.task('build', ['build-core', 'build-shortcuts', 'build-debug'])
gulp.task('watch', function() {
gulp.watch(jsFiles, ['lint', 'build'])
})
gulp.task('default', ['lint', 'build', 'watch'])
================================================
FILE: lib/jquery.waypoints.js
================================================
/*!
Waypoints - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
(function() {
'use strict'
var keyCounter = 0
var allWaypoints = {}
/* http://imakewebthings.com/waypoints/api/waypoint */
function Waypoint(options) {
if (!options) {
throw new Error('No options passed to Waypoint constructor')
}
if (!options.element) {
throw new Error('No element option passed to Waypoint constructor')
}
if (!options.handler) {
throw new Error('No handler option passed to Waypoint constructor')
}
this.key = 'waypoint-' + keyCounter
this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
this.element = this.options.element
this.adapter = new Waypoint.Adapter(this.element)
this.callback = options.handler
this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
this.enabled = this.options.enabled
this.triggerPoint = null
this.group = Waypoint.Group.findOrCreate({
name: this.options.group,
axis: this.axis
})
this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
if (Waypoint.offsetAliases[this.options.offset]) {
this.options.offset = Waypoint.offsetAliases[this.options.offset]
}
this.group.add(this)
this.context.add(this)
allWaypoints[this.key] = this
keyCounter += 1
}
/* Private */
Waypoint.prototype.queueTrigger = function(direction) {
this.group.queueTrigger(this, direction)
}
/* Private */
Waypoint.prototype.trigger = function(args) {
if (!this.enabled) {
return
}
if (this.callback) {
this.callback.apply(this, args)
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/destroy */
Waypoint.prototype.destroy = function() {
this.context.remove(this)
this.group.remove(this)
delete allWaypoints[this.key]
}
/* Public */
/* http://imakewebthings.com/waypoints/api/disable */
Waypoint.prototype.disable = function() {
this.enabled = false
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/enable */
Waypoint.prototype.enable = function() {
this.context.refresh()
this.enabled = true
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/next */
Waypoint.prototype.next = function() {
return this.group.next(this)
}
/* Public */
/* http://imakewebthings.com/waypoints/api/previous */
Waypoint.prototype.previous = function() {
return this.group.previous(this)
}
/* Private */
Waypoint.invokeAll = function(method) {
var allWaypointsArray = []
for (var waypointKey in allWaypoints) {
allWaypointsArray.push(allWaypoints[waypointKey])
}
for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
allWaypointsArray[i][method]()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/destroy-all */
Waypoint.destroyAll = function() {
Waypoint.invokeAll('destroy')
}
/* Public */
/* http://imakewebthings.com/waypoints/api/disable-all */
Waypoint.disableAll = function() {
Waypoint.invokeAll('disable')
}
/* Public */
/* http://imakewebthings.com/waypoints/api/enable-all */
Waypoint.enableAll = function() {
Waypoint.Context.refreshAll()
for (var waypointKey in allWaypoints) {
allWaypoints[waypointKey].enabled = true
}
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/refresh-all */
Waypoint.refreshAll = function() {
Waypoint.Context.refreshAll()
}
/* Public */
/* http://imakewebthings.com/waypoints/api/viewport-height */
Waypoint.viewportHeight = function() {
return window.innerHeight || document.documentElement.clientHeight
}
/* Public */
/* http://imakewebthings.com/waypoints/api/viewport-width */
Waypoint.viewportWidth = function() {
return document.documentElement.clientWidth
}
Waypoint.adapters = []
Waypoint.defaults = {
context: window,
continuous: true,
enabled: true,
group: 'default',
horizontal: false,
offset: 0
}
Waypoint.offsetAliases = {
'bottom-in-view': function() {
return this.context.innerHeight() - this.adapter.outerHeight()
},
'right-in-view': function() {
return this.context.innerWidth() - this.adapter.outerWidth()
}
}
window.Waypoint = Waypoint
}())
;(function() {
'use strict'
function requestAnimationFrameShim(callback) {
window.setTimeout(callback, 1000 / 60)
}
var keyCounter = 0
var contexts = {}
var Waypoint = window.Waypoint
var oldWindowLoad = window.onload
/* http://imakewebthings.com/waypoints/api/context */
function Context(element) {
this.element = element
this.Adapter = Waypoint.Adapter
this.adapter = new this.Adapter(element)
this.key = 'waypoint-context-' + keyCounter
this.didScroll = false
this.didResize = false
this.oldScroll = {
x: this.adapter.scrollLeft(),
y: this.adapter.scrollTop()
}
this.waypoints = {
vertical: {},
horizontal: {}
}
element.waypointContextKey = this.key
contexts[element.waypointContextKey] = this
keyCounter += 1
if (!Waypoint.windowContext) {
Waypoint.windowContext = true
Waypoint.windowContext = new Context(window)
}
this.createThrottledScrollHandler()
this.createThrottledResizeHandler()
}
/* Private */
Context.prototype.add = function(waypoint) {
var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
this.waypoints[axis][waypoint.key] = waypoint
this.refresh()
}
/* Private */
Context.prototype.checkEmpty = function() {
var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
var isWindow = this.element == this.element.window
if (horizontalEmpty && verticalEmpty && !isWindow) {
this.adapter.off('.waypoints')
delete contexts[this.key]
}
}
/* Private */
Context.prototype.createThrottledResizeHandler = function() {
var self = this
function resizeHandler() {
self.handleResize()
self.didResize = false
}
this.adapter.on('resize.waypoints', function() {
if (!self.didResize) {
self.didResize = true
Waypoint.requestAnimationFrame(resizeHandler)
}
})
}
/* Private */
Context.prototype.createThrottledScrollHandler = function() {
var self = this
function scrollHandler() {
self.handleScroll()
self.didScroll = false
}
this.adapter.on('scroll.waypoints', function() {
if (!self.didScroll || Waypoint.isTouch) {
self.didScroll = true
Waypoint.requestAnimationFrame(scrollHandler)
}
})
}
/* Private */
Context.prototype.handleResize = function() {
Waypoint.Context.refreshAll()
}
/* Private */
Context.prototype.handleScroll = function() {
var triggeredGroups = {}
var axes = {
horizontal: {
newScroll: this.adapter.scrollLeft(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left'
},
vertical: {
newScroll: this.adapter.scrollTop(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up'
}
}
for (var axisKey in axes) {
var axis = axes[axisKey]
var isForward = axis.newScroll > axis.oldScroll
var direction = isForward ? axis.forward : axis.backward
for (var waypointKey in this.waypoints[axisKey]) {
var waypoint = this.waypoints[axisKey][waypointKey]
if (waypoint.triggerPoint === null) {
continue
}
var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
if (crossedForward || crossedBackward) {
waypoint.queueTrigger(direction)
triggeredGroups[waypoint.group.id] = waypoint.group
}
}
}
for (var groupKey in triggeredGroups) {
triggeredGroups[groupKey].flushTriggers()
}
this.oldScroll = {
x: axes.horizontal.newScroll,
y: axes.vertical.newScroll
}
}
/* Private */
Context.prototype.innerHeight = function() {
/*eslint-disable eqeqeq */
if (this.element == this.element.window) {
return Waypoint.viewportHeight()
}
/*eslint-enable eqeqeq */
return this.adapter.innerHeight()
}
/* Private */
Context.prototype.remove = function(waypoint) {
delete this.waypoints[waypoint.axis][waypoint.key]
this.checkEmpty()
}
/* Private */
Context.prototype.innerWidth = function() {
/*eslint-disable eqeqeq */
if (this.element == this.element.window) {
return Waypoint.viewportWidth()
}
/*eslint-enable eqeqeq */
return this.adapter.innerWidth()
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-destroy */
Context.prototype.destroy = function() {
var allWaypoints = []
for (var axis in this.waypoints) {
for (var waypointKey in this.waypoints[axis]) {
allWaypoints.push(this.waypoints[axis][waypointKey])
}
}
for (var i = 0, end = allWaypoints.length; i < end; i++) {
allWaypoints[i].destroy()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-refresh */
Context.prototype.refresh = function() {
/*eslint-disable eqeqeq */
var isWindow = this.element == this.element.window
/*eslint-enable eqeqeq */
var contextOffset = isWindow ? undefined : this.adapter.offset()
var triggeredGroups = {}
var axes
this.handleScroll()
axes = {
horizontal: {
contextOffset: isWindow ? 0 : contextOffset.left,
contextScroll: isWindow ? 0 : this.oldScroll.x,
contextDimension: this.innerWidth(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left',
offsetProp: 'left'
},
vertical: {
contextOffset: isWindow ? 0 : contextOffset.top,
contextScroll: isWindow ? 0 : this.oldScroll.y,
contextDimension: this.innerHeight(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up',
offsetProp: 'top'
}
}
for (var axisKey in axes) {
var axis = axes[axisKey]
for (var waypointKey in this.waypoints[axisKey]) {
var waypoint = this.waypoints[axisKey][waypointKey]
var adjustment = waypoint.options.offset
var oldTriggerPoint = waypoint.triggerPoint
var elementOffset = 0
var freshWaypoint = oldTriggerPoint == null
var contextModifier, wasBeforeScroll, nowAfterScroll
var triggeredBackward, triggeredForward
if (waypoint.element !== waypoint.element.window) {
elementOffset = waypoint.adapter.offset()[axis.offsetProp]
}
if (typeof adjustment === 'function') {
adjustment = adjustment.apply(waypoint)
}
else if (typeof adjustment === 'string') {
adjustment = parseFloat(adjustment)
if (waypoint.options.offset.indexOf('%') > - 1) {
adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
}
}
contextModifier = axis.contextScroll - axis.contextOffset
waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
wasBeforeScroll = oldTriggerPoint < axis.oldScroll
nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
triggeredBackward = wasBeforeScroll && nowAfterScroll
triggeredForward = !wasBeforeScroll && !nowAfterScroll
if (!freshWaypoint && triggeredBackward) {
waypoint.queueTrigger(axis.backward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
else if (!freshWaypoint && triggeredForward) {
waypoint.queueTrigger(axis.forward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
waypoint.queueTrigger(axis.forward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
}
}
Waypoint.requestAnimationFrame(function() {
for (var groupKey in triggeredGroups) {
triggeredGroups[groupKey].flushTriggers()
}
})
return this
}
/* Private */
Context.findOrCreateByElement = function(element) {
return Context.findByElement(element) || new Context(element)
}
/* Private */
Context.refreshAll = function() {
for (var contextId in contexts) {
contexts[contextId].refresh()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-find-by-element */
Context.findByElement = function(element) {
return contexts[element.waypointContextKey]
}
window.onload = function() {
if (oldWindowLoad) {
oldWindowLoad()
}
Context.refreshAll()
}
Waypoint.requestAnimationFrame = function(callback) {
var requestFn = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
requestAnimationFrameShim
requestFn.call(window, callback)
}
Waypoint.Context = Context
}())
;(function() {
'use strict'
function byTriggerPoint(a, b) {
return a.triggerPoint - b.triggerPoint
}
function byReverseTriggerPoint(a, b) {
return b.triggerPoint - a.triggerPoint
}
var groups = {
vertical: {},
horizontal: {}
}
var Waypoint = window.Waypoint
/* http://imakewebthings.com/waypoints/api/group */
function Group(options) {
this.name = options.name
this.axis = options.axis
this.id = this.name + '-' + this.axis
this.waypoints = []
this.clearTriggerQueues()
groups[this.axis][this.name] = this
}
/* Private */
Group.prototype.add = function(waypoint) {
this.waypoints.push(waypoint)
}
/* Private */
Group.prototype.clearTriggerQueues = function() {
this.triggerQueues = {
up: [],
down: [],
left: [],
right: []
}
}
/* Private */
Group.prototype.flushTriggers = function() {
for (var direction in this.triggerQueues) {
var waypoints = this.triggerQueues[direction]
var reverse = direction === 'up' || direction === 'left'
waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
for (var i = 0, end = waypoints.length; i < end; i += 1) {
var waypoint = waypoints[i]
if (waypoint.options.continuous || i === waypoints.length - 1) {
waypoint.trigger([direction])
}
}
}
this.clearTriggerQueues()
}
/* Private */
Group.prototype.next = function(waypoint) {
this.waypoints.sort(byTriggerPoint)
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
var isLast = index === this.waypoints.length - 1
return isLast ? null : this.waypoints[index + 1]
}
/* Private */
Group.prototype.previous = function(waypoint) {
this.waypoints.sort(byTriggerPoint)
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
return index ? this.waypoints[index - 1] : null
}
/* Private */
Group.prototype.queueTrigger = function(waypoint, direction) {
this.triggerQueues[direction].push(waypoint)
}
/* Private */
Group.prototype.remove = function(waypoint) {
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
if (index > -1) {
this.waypoints.splice(index, 1)
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/first */
Group.prototype.first = function() {
return this.waypoints[0]
}
/* Public */
/* http://imakewebthings.com/waypoints/api/last */
Group.prototype.last = function() {
return this.waypoints[this.waypoints.length - 1]
}
/* Private */
Group.findOrCreate = function(options) {
return groups[options.axis][options.name] || new Group(options)
}
Waypoint.Group = Group
}())
;(function() {
'use strict'
var $ = window.jQuery
var Waypoint = window.Waypoint
function JQueryAdapter(element) {
this.$element = $(element)
}
$.each([
'innerHeight',
'innerWidth',
'off',
'offset',
'on',
'outerHeight',
'outerWidth',
'scrollLeft',
'scrollTop'
], function(i, method) {
JQueryAdapter.prototype[method] = function() {
var args = Array.prototype.slice.call(arguments)
return this.$element[method].apply(this.$element, args)
}
})
$.each([
'extend',
'inArray',
'isEmptyObject'
], function(i, method) {
JQueryAdapter[method] = $[method]
})
Waypoint.adapters.push({
name: 'jquery',
Adapter: JQueryAdapter
})
Waypoint.Adapter = JQueryAdapter
}())
;(function() {
'use strict'
var Waypoint = window.Waypoint
function createExtension(framework) {
return function() {
var waypoints = []
var overrides = arguments[0]
if (framework.isFunction(arguments[0])) {
overrides = framework.extend({}, arguments[1])
overrides.handler = arguments[0]
}
this.each(function() {
var options = framework.extend({}, overrides, {
element: this
})
if (typeof options.context === 'string') {
options.context = framework(this).closest(options.context)[0]
}
waypoints.push(new Waypoint(options))
})
return waypoints
}
}
if (window.jQuery) {
window.jQuery.fn.waypoint = createExtension(window.jQuery)
}
if (window.Zepto) {
window.Zepto.fn.waypoint = createExtension(window.Zepto)
}
}())
;
================================================
FILE: lib/noframework.waypoints.js
================================================
/*!
Waypoints - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
(function() {
'use strict'
var keyCounter = 0
var allWaypoints = {}
/* http://imakewebthings.com/waypoints/api/waypoint */
function Waypoint(options) {
if (!options) {
throw new Error('No options passed to Waypoint constructor')
}
if (!options.element) {
throw new Error('No element option passed to Waypoint constructor')
}
if (!options.handler) {
throw new Error('No handler option passed to Waypoint constructor')
}
this.key = 'waypoint-' + keyCounter
this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
this.element = this.options.element
this.adapter = new Waypoint.Adapter(this.element)
this.callback = options.handler
this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
this.enabled = this.options.enabled
this.triggerPoint = null
this.group = Waypoint.Group.findOrCreate({
name: this.options.group,
axis: this.axis
})
this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
if (Waypoint.offsetAliases[this.options.offset]) {
this.options.offset = Waypoint.offsetAliases[this.options.offset]
}
this.group.add(this)
this.context.add(this)
allWaypoints[this.key] = this
keyCounter += 1
}
/* Private */
Waypoint.prototype.queueTrigger = function(direction) {
this.group.queueTrigger(this, direction)
}
/* Private */
Waypoint.prototype.trigger = function(args) {
if (!this.enabled) {
return
}
if (this.callback) {
this.callback.apply(this, args)
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/destroy */
Waypoint.prototype.destroy = function() {
this.context.remove(this)
this.group.remove(this)
delete allWaypoints[this.key]
}
/* Public */
/* http://imakewebthings.com/waypoints/api/disable */
Waypoint.prototype.disable = function() {
this.enabled = false
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/enable */
Waypoint.prototype.enable = function() {
this.context.refresh()
this.enabled = true
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/next */
Waypoint.prototype.next = function() {
return this.group.next(this)
}
/* Public */
/* http://imakewebthings.com/waypoints/api/previous */
Waypoint.prototype.previous = function() {
return this.group.previous(this)
}
/* Private */
Waypoint.invokeAll = function(method) {
var allWaypointsArray = []
for (var waypointKey in allWaypoints) {
allWaypointsArray.push(allWaypoints[waypointKey])
}
for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
allWaypointsArray[i][method]()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/destroy-all */
Waypoint.destroyAll = function() {
Waypoint.invokeAll('destroy')
}
/* Public */
/* http://imakewebthings.com/waypoints/api/disable-all */
Waypoint.disableAll = function() {
Waypoint.invokeAll('disable')
}
/* Public */
/* http://imakewebthings.com/waypoints/api/enable-all */
Waypoint.enableAll = function() {
Waypoint.Context.refreshAll()
for (var waypointKey in allWaypoints) {
allWaypoints[waypointKey].enabled = true
}
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/refresh-all */
Waypoint.refreshAll = function() {
Waypoint.Context.refreshAll()
}
/* Public */
/* http://imakewebthings.com/waypoints/api/viewport-height */
Waypoint.viewportHeight = function() {
return window.innerHeight || document.documentElement.clientHeight
}
/* Public */
/* http://imakewebthings.com/waypoints/api/viewport-width */
Waypoint.viewportWidth = function() {
return document.documentElement.clientWidth
}
Waypoint.adapters = []
Waypoint.defaults = {
context: window,
continuous: true,
enabled: true,
group: 'default',
horizontal: false,
offset: 0
}
Waypoint.offsetAliases = {
'bottom-in-view': function() {
return this.context.innerHeight() - this.adapter.outerHeight()
},
'right-in-view': function() {
return this.context.innerWidth() - this.adapter.outerWidth()
}
}
window.Waypoint = Waypoint
}())
;(function() {
'use strict'
function requestAnimationFrameShim(callback) {
window.setTimeout(callback, 1000 / 60)
}
var keyCounter = 0
var contexts = {}
var Waypoint = window.Waypoint
var oldWindowLoad = window.onload
/* http://imakewebthings.com/waypoints/api/context */
function Context(element) {
this.element = element
this.Adapter = Waypoint.Adapter
this.adapter = new this.Adapter(element)
this.key = 'waypoint-context-' + keyCounter
this.didScroll = false
this.didResize = false
this.oldScroll = {
x: this.adapter.scrollLeft(),
y: this.adapter.scrollTop()
}
this.waypoints = {
vertical: {},
horizontal: {}
}
element.waypointContextKey = this.key
contexts[element.waypointContextKey] = this
keyCounter += 1
if (!Waypoint.windowContext) {
Waypoint.windowContext = true
Waypoint.windowContext = new Context(window)
}
this.createThrottledScrollHandler()
this.createThrottledResizeHandler()
}
/* Private */
Context.prototype.add = function(waypoint) {
var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
this.waypoints[axis][waypoint.key] = waypoint
this.refresh()
}
/* Private */
Context.prototype.checkEmpty = function() {
var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
var isWindow = this.element == this.element.window
if (horizontalEmpty && verticalEmpty && !isWindow) {
this.adapter.off('.waypoints')
delete contexts[this.key]
}
}
/* Private */
Context.prototype.createThrottledResizeHandler = function() {
var self = this
function resizeHandler() {
self.handleResize()
self.didResize = false
}
this.adapter.on('resize.waypoints', function() {
if (!self.didResize) {
self.didResize = true
Waypoint.requestAnimationFrame(resizeHandler)
}
})
}
/* Private */
Context.prototype.createThrottledScrollHandler = function() {
var self = this
function scrollHandler() {
self.handleScroll()
self.didScroll = false
}
this.adapter.on('scroll.waypoints', function() {
if (!self.didScroll || Waypoint.isTouch) {
self.didScroll = true
Waypoint.requestAnimationFrame(scrollHandler)
}
})
}
/* Private */
Context.prototype.handleResize = function() {
Waypoint.Context.refreshAll()
}
/* Private */
Context.prototype.handleScroll = function() {
var triggeredGroups = {}
var axes = {
horizontal: {
newScroll: this.adapter.scrollLeft(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left'
},
vertical: {
newScroll: this.adapter.scrollTop(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up'
}
}
for (var axisKey in axes) {
var axis = axes[axisKey]
var isForward = axis.newScroll > axis.oldScroll
var direction = isForward ? axis.forward : axis.backward
for (var waypointKey in this.waypoints[axisKey]) {
var waypoint = this.waypoints[axisKey][waypointKey]
if (waypoint.triggerPoint === null) {
continue
}
var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
if (crossedForward || crossedBackward) {
waypoint.queueTrigger(direction)
triggeredGroups[waypoint.group.id] = waypoint.group
}
}
}
for (var groupKey in triggeredGroups) {
triggeredGroups[groupKey].flushTriggers()
}
this.oldScroll = {
x: axes.horizontal.newScroll,
y: axes.vertical.newScroll
}
}
/* Private */
Context.prototype.innerHeight = function() {
/*eslint-disable eqeqeq */
if (this.element == this.element.window) {
return Waypoint.viewportHeight()
}
/*eslint-enable eqeqeq */
return this.adapter.innerHeight()
}
/* Private */
Context.prototype.remove = function(waypoint) {
delete this.waypoints[waypoint.axis][waypoint.key]
this.checkEmpty()
}
/* Private */
Context.prototype.innerWidth = function() {
/*eslint-disable eqeqeq */
if (this.element == this.element.window) {
return Waypoint.viewportWidth()
}
/*eslint-enable eqeqeq */
return this.adapter.innerWidth()
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-destroy */
Context.prototype.destroy = function() {
var allWaypoints = []
for (var axis in this.waypoints) {
for (var waypointKey in this.waypoints[axis]) {
allWaypoints.push(this.waypoints[axis][waypointKey])
}
}
for (var i = 0, end = allWaypoints.length; i < end; i++) {
allWaypoints[i].destroy()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-refresh */
Context.prototype.refresh = function() {
/*eslint-disable eqeqeq */
var isWindow = this.element == this.element.window
/*eslint-enable eqeqeq */
var contextOffset = isWindow ? undefined : this.adapter.offset()
var triggeredGroups = {}
var axes
this.handleScroll()
axes = {
horizontal: {
contextOffset: isWindow ? 0 : contextOffset.left,
contextScroll: isWindow ? 0 : this.oldScroll.x,
contextDimension: this.innerWidth(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left',
offsetProp: 'left'
},
vertical: {
contextOffset: isWindow ? 0 : contextOffset.top,
contextScroll: isWindow ? 0 : this.oldScroll.y,
contextDimension: this.innerHeight(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up',
offsetProp: 'top'
}
}
for (var axisKey in axes) {
var axis = axes[axisKey]
for (var waypointKey in this.waypoints[axisKey]) {
var waypoint = this.waypoints[axisKey][waypointKey]
var adjustment = waypoint.options.offset
var oldTriggerPoint = waypoint.triggerPoint
var elementOffset = 0
var freshWaypoint = oldTriggerPoint == null
var contextModifier, wasBeforeScroll, nowAfterScroll
var triggeredBackward, triggeredForward
if (waypoint.element !== waypoint.element.window) {
elementOffset = waypoint.adapter.offset()[axis.offsetProp]
}
if (typeof adjustment === 'function') {
adjustment = adjustment.apply(waypoint)
}
else if (typeof adjustment === 'string') {
adjustment = parseFloat(adjustment)
if (waypoint.options.offset.indexOf('%') > - 1) {
adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
}
}
contextModifier = axis.contextScroll - axis.contextOffset
waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
wasBeforeScroll = oldTriggerPoint < axis.oldScroll
nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
triggeredBackward = wasBeforeScroll && nowAfterScroll
triggeredForward = !wasBeforeScroll && !nowAfterScroll
if (!freshWaypoint && triggeredBackward) {
waypoint.queueTrigger(axis.backward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
else if (!freshWaypoint && triggeredForward) {
waypoint.queueTrigger(axis.forward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
waypoint.queueTrigger(axis.forward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
}
}
Waypoint.requestAnimationFrame(function() {
for (var groupKey in triggeredGroups) {
triggeredGroups[groupKey].flushTriggers()
}
})
return this
}
/* Private */
Context.findOrCreateByElement = function(element) {
return Context.findByElement(element) || new Context(element)
}
/* Private */
Context.refreshAll = function() {
for (var contextId in contexts) {
contexts[contextId].refresh()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-find-by-element */
Context.findByElement = function(element) {
return contexts[element.waypointContextKey]
}
window.onload = function() {
if (oldWindowLoad) {
oldWindowLoad()
}
Context.refreshAll()
}
Waypoint.requestAnimationFrame = function(callback) {
var requestFn = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
requestAnimationFrameShim
requestFn.call(window, callback)
}
Waypoint.Context = Context
}())
;(function() {
'use strict'
function byTriggerPoint(a, b) {
return a.triggerPoint - b.triggerPoint
}
function byReverseTriggerPoint(a, b) {
return b.triggerPoint - a.triggerPoint
}
var groups = {
vertical: {},
horizontal: {}
}
var Waypoint = window.Waypoint
/* http://imakewebthings.com/waypoints/api/group */
function Group(options) {
this.name = options.name
this.axis = options.axis
this.id = this.name + '-' + this.axis
this.waypoints = []
this.clearTriggerQueues()
groups[this.axis][this.name] = this
}
/* Private */
Group.prototype.add = function(waypoint) {
this.waypoints.push(waypoint)
}
/* Private */
Group.prototype.clearTriggerQueues = function() {
this.triggerQueues = {
up: [],
down: [],
left: [],
right: []
}
}
/* Private */
Group.prototype.flushTriggers = function() {
for (var direction in this.triggerQueues) {
var waypoints = this.triggerQueues[direction]
var reverse = direction === 'up' || direction === 'left'
waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
for (var i = 0, end = waypoints.length; i < end; i += 1) {
var waypoint = waypoints[i]
if (waypoint.options.continuous || i === waypoints.length - 1) {
waypoint.trigger([direction])
}
}
}
this.clearTriggerQueues()
}
/* Private */
Group.prototype.next = function(waypoint) {
this.waypoints.sort(byTriggerPoint)
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
var isLast = index === this.waypoints.length - 1
return isLast ? null : this.waypoints[index + 1]
}
/* Private */
Group.prototype.previous = function(waypoint) {
this.waypoints.sort(byTriggerPoint)
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
return index ? this.waypoints[index - 1] : null
}
/* Private */
Group.prototype.queueTrigger = function(waypoint, direction) {
this.triggerQueues[direction].push(waypoint)
}
/* Private */
Group.prototype.remove = function(waypoint) {
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
if (index > -1) {
this.waypoints.splice(index, 1)
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/first */
Group.prototype.first = function() {
return this.waypoints[0]
}
/* Public */
/* http://imakewebthings.com/waypoints/api/last */
Group.prototype.last = function() {
return this.waypoints[this.waypoints.length - 1]
}
/* Private */
Group.findOrCreate = function(options) {
return groups[options.axis][options.name] || new Group(options)
}
Waypoint.Group = Group
}())
;(function() {
'use strict'
var Waypoint = window.Waypoint
function isWindow(element) {
return element === element.window
}
function getWindow(element) {
if (isWindow(element)) {
return element
}
return element.defaultView
}
function NoFrameworkAdapter(element) {
this.element = element
this.handlers = {}
}
NoFrameworkAdapter.prototype.innerHeight = function() {
var isWin = isWindow(this.element)
return isWin ? this.element.innerHeight : this.element.clientHeight
}
NoFrameworkAdapter.prototype.innerWidth = function() {
var isWin = isWindow(this.element)
return isWin ? this.element.innerWidth : this.element.clientWidth
}
NoFrameworkAdapter.prototype.off = function(event, handler) {
function removeListeners(element, listeners, handler) {
for (var i = 0, end = listeners.length - 1; i < end; i++) {
var listener = listeners[i]
if (!handler || handler === listener) {
element.removeEventListener(listener)
}
}
}
var eventParts = event.split('.')
var eventType = eventParts[0]
var namespace = eventParts[1]
var element = this.element
if (namespace && this.handlers[namespace] && eventType) {
removeListeners(element, this.handlers[namespace][eventType], handler)
this.handlers[namespace][eventType] = []
}
else if (eventType) {
for (var ns in this.handlers) {
removeListeners(element, this.handlers[ns][eventType] || [], handler)
this.handlers[ns][eventType] = []
}
}
else if (namespace && this.handlers[namespace]) {
for (var type in this.handlers[namespace]) {
removeListeners(element, this.handlers[namespace][type], handler)
}
this.handlers[namespace] = {}
}
}
/* Adapted from jQuery 1.x offset() */
NoFrameworkAdapter.prototype.offset = function() {
if (!this.element.ownerDocument) {
return null
}
var documentElement = this.element.ownerDocument.documentElement
var win = getWindow(this.element.ownerDocument)
var rect = {
top: 0,
left: 0
}
if (this.element.getBoundingClientRect) {
rect = this.element.getBoundingClientRect()
}
return {
top: rect.top + win.pageYOffset - documentElement.clientTop,
left: rect.left + win.pageXOffset - documentElement.clientLeft
}
}
NoFrameworkAdapter.prototype.on = function(event, handler) {
var eventParts = event.split('.')
var eventType = eventParts[0]
var namespace = eventParts[1] || '__default'
var nsHandlers = this.handlers[namespace] = this.handlers[namespace] || {}
var nsTypeList = nsHandlers[eventType] = nsHandlers[eventType] || []
nsTypeList.push(handler)
this.element.addEventListener(eventType, handler)
}
NoFrameworkAdapter.prototype.outerHeight = function(includeMargin) {
var height = this.innerHeight()
var computedStyle
if (includeMargin && !isWindow(this.element)) {
computedStyle = window.getComputedStyle(this.element)
height += parseInt(computedStyle.marginTop, 10)
height += parseInt(computedStyle.marginBottom, 10)
}
return height
}
NoFrameworkAdapter.prototype.outerWidth = function(includeMargin) {
var width = this.innerWidth()
var computedStyle
if (includeMargin && !isWindow(this.element)) {
computedStyle = window.getComputedStyle(this.element)
width += parseInt(computedStyle.marginLeft, 10)
width += parseInt(computedStyle.marginRight, 10)
}
return width
}
NoFrameworkAdapter.prototype.scrollLeft = function() {
var win = getWindow(this.element)
return win ? win.pageXOffset : this.element.scrollLeft
}
NoFrameworkAdapter.prototype.scrollTop = function() {
var win = getWindow(this.element)
return win ? win.pageYOffset : this.element.scrollTop
}
NoFrameworkAdapter.extend = function() {
var args = Array.prototype.slice.call(arguments)
function merge(target, obj) {
if (typeof target === 'object' && typeof obj === 'object') {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = obj[key]
}
}
}
return target
}
for (var i = 1, end = args.length; i < end; i++) {
merge(args[0], args[i])
}
return args[0]
}
NoFrameworkAdapter.inArray = function(element, array, i) {
return array == null ? -1 : array.indexOf(element, i)
}
NoFrameworkAdapter.isEmptyObject = function(obj) {
/* eslint no-unused-vars: 0 */
for (var name in obj) {
return false
}
return true
}
Waypoint.adapters.push({
name: 'noframework',
Adapter: NoFrameworkAdapter
})
Waypoint.Adapter = NoFrameworkAdapter
}())
;
================================================
FILE: lib/shortcuts/infinite.js
================================================
/*!
Waypoints Infinite Scroll Shortcut - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
(function() {
'use strict'
var $ = window.jQuery
var Waypoint = window.Waypoint
/* http://imakewebthings.com/waypoints/shortcuts/infinite-scroll */
function Infinite(options) {
this.options = $.extend({}, Infinite.defaults, options)
this.container = this.options.element
if (this.options.container !== 'auto') {
this.container = this.options.container
}
this.$container = $(this.container)
this.$more = $(this.options.more)
if (this.$more.length) {
this.setupHandler()
this.waypoint = new Waypoint(this.options)
}
}
/* Private */
Infinite.prototype.setupHandler = function() {
this.options.handler = $.proxy(function() {
this.options.onBeforePageLoad()
this.destroy()
this.$container.addClass(this.options.loadingClass)
$.get($(this.options.more).attr('href'), $.proxy(function(data) {
var $data = $($.parseHTML(data))
var $newMore = $data.find(this.options.more)
var $items = $data.find(this.options.items)
if (!$items.length) {
$items = $data.filter(this.options.items)
}
this.$container.append($items)
this.$container.removeClass(this.options.loadingClass)
if (!$newMore.length) {
$newMore = $data.filter(this.options.more)
}
if ($newMore.length) {
this.$more.replaceWith($newMore)
this.$more = $newMore
this.waypoint = new Waypoint(this.options)
}
else {
this.$more.remove()
}
this.options.onAfterPageLoad($items)
}, this))
}, this)
}
/* Public */
Infinite.prototype.destroy = function() {
if (this.waypoint) {
this.waypoint.destroy()
}
}
Infinite.defaults = {
container: 'auto',
items: '.infinite-item',
more: '.infinite-more-link',
offset: 'bottom-in-view',
loadingClass: 'infinite-loading',
onBeforePageLoad: $.noop,
onAfterPageLoad: $.noop
}
Waypoint.Infinite = Infinite
}())
;
================================================
FILE: lib/shortcuts/inview.js
================================================
/*!
Waypoints Inview Shortcut - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
(function() {
'use strict'
function noop() {}
var Waypoint = window.Waypoint
/* http://imakewebthings.com/waypoints/shortcuts/inview */
function Inview(options) {
this.options = Waypoint.Adapter.extend({}, Inview.defaults, options)
this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
this.waypoints = []
this.element = this.options.element
this.createWaypoints()
}
/* Private */
Inview.prototype.createWaypoints = function() {
var configs = {
vertical: [{
down: 'enter',
up: 'exited',
offset: '100%'
}, {
down: 'entered',
up: 'exit',
offset: 'bottom-in-view'
}, {
down: 'exit',
up: 'entered',
offset: 0
}, {
down: 'exited',
up: 'enter',
offset: function() {
return -this.adapter.outerHeight()
}
}],
horizontal: [{
right: 'enter',
left: 'exited',
offset: '100%'
}, {
right: 'entered',
left: 'exit',
offset: 'right-in-view'
}, {
right: 'exit',
left: 'entered',
offset: 0
}, {
right: 'exited',
left: 'enter',
offset: function() {
return -this.adapter.outerWidth()
}
}]
}
for (var i = 0, end = configs[this.axis].length; i < end; i++) {
var config = configs[this.axis][i]
this.createWaypoint(config)
}
}
/* Private */
Inview.prototype.createWaypoint = function(config) {
var self = this
this.waypoints.push(new Waypoint({
context: this.options.context,
element: this.options.element,
enabled: this.options.enabled,
handler: (function(config) {
return function(direction) {
self.options[config[direction]].call(self, direction)
}
}(config)),
offset: config.offset,
horizontal: this.options.horizontal
}))
}
/* Public */
Inview.prototype.destroy = function() {
for (var i = 0, end = this.waypoints.length; i < end; i++) {
this.waypoints[i].destroy()
}
this.waypoints = []
}
Inview.prototype.disable = function() {
for (var i = 0, end = this.waypoints.length; i < end; i++) {
this.waypoints[i].disable()
}
}
Inview.prototype.enable = function() {
for (var i = 0, end = this.waypoints.length; i < end; i++) {
this.waypoints[i].enable()
}
}
Inview.defaults = {
context: window,
enabled: true,
enter: noop,
entered: noop,
exit: noop,
exited: noop
}
Waypoint.Inview = Inview
}())
;
================================================
FILE: lib/shortcuts/sticky.js
================================================
/*!
Waypoints Sticky Element Shortcut - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
(function() {
'use strict'
var $ = window.jQuery
var Waypoint = window.Waypoint
/* http://imakewebthings.com/waypoints/shortcuts/sticky-elements */
function Sticky(options) {
this.options = $.extend({}, Waypoint.defaults, Sticky.defaults, options)
this.element = this.options.element
this.$element = $(this.element)
this.createWrapper()
this.createWaypoint()
}
/* Private */
Sticky.prototype.createWaypoint = function() {
var originalHandler = this.options.handler
this.waypoint = new Waypoint($.extend({}, this.options, {
element: this.wrapper,
handler: $.proxy(function(direction) {
var shouldBeStuck = this.options.direction.indexOf(direction) > -1
var wrapperHeight = shouldBeStuck ? this.$element.outerHeight(true) : ''
this.$wrapper.height(wrapperHeight)
this.$element.toggleClass(this.options.stuckClass, shouldBeStuck)
if (originalHandler) {
originalHandler.call(this, direction)
}
}, this)
}))
}
/* Private */
Sticky.prototype.createWrapper = function() {
if (this.options.wrapper) {
this.$element.wrap(this.options.wrapper)
}
this.$wrapper = this.$element.parent()
this.wrapper = this.$wrapper[0]
}
/* Public */
Sticky.prototype.destroy = function() {
if (this.$element.parent()[0] === this.wrapper) {
this.waypoint.destroy()
this.$element.removeClass(this.options.stuckClass)
if (this.options.wrapper) {
this.$element.unwrap()
}
}
}
Sticky.defaults = {
wrapper: '
=0)throw SyntaxError('multiple object literal properties named "'+l+'"');m.push(l)}}if(!n.length)return this.front?"({})":"{}";if(this.generated)for(p=0,r=n.length;p=0?"[\n"+a.indent+b+"\n"+this.tab+"]":"["+b+"]"},b.prototype.assigns=function(a){var b,c,d,e;e=this.objects;for(c=0,d=e.length;c =0)throw SyntaxError("variable name may not be "+a);return a&&(a=o.test(a)&&a)},c.prototype.setContext=function(a){return this.body.traverseChildren(!1,function(b){if(b.classBody)return!1;if(b instanceof A&&b.value==="this")return b.value=a;if(b instanceof j){b.klass=a;if(b.bound)return b.context=a}})},c.prototype.addBoundFunctions=function(a){var c,d,e,f,g,h;if(this.boundFuncs.length){g=this.boundFuncs,h=[];for(e=0,f=g.length;e =0);if(e&&this.context!=="object")throw SyntaxError('variable name may not be "'+f+'"')}bl(c,a),c.prototype.children=["variable","value"],c.prototype.isStatement=function(a){return(a!=null?a.level:void 0)===z&&this.context!=null&&bm.call(this.context,"?")>=0},c.prototype.assigns=function(a){return this[this.context==="object"?"value":"variable"].assigns(a)},c.prototype.unfoldSoak=function(a){return bg(a,this,"variable")},c.prototype.compileNode=function(a){var b,c,d,e,f,g,h,i,k;if(b=this.variable instanceof W){if(this.variable.isArray()||this.variable.isObject())return this.compilePatternMatch(a);if(this.variable.isSplice())return this.compileSplice(a);if((g=this.context)==="||="||g==="&&="||g==="?=")return this.compileConditional(a)}d=this.variable.compile(a,w);if(!this.context){if(!(f=this.variable.unwrapAll()).isAssignable())throw SyntaxError('"'+this.variable.compile(a)+'" cannot be assigned.');if(typeof f.hasProperties=="function"?!f.hasProperties():!void 0)this.param?a.scope.add(d,"var"):a.scope.find(d)}this.value instanceof j&&(c=B.exec(d))&&(c[1]&&(this.value.klass=c[1]),this.value.name=(h=(i=(k=c[2])!=null?k:c[3])!=null?i:c[4])!=null?h:c[5]),e=this.value.compile(a,w);if(this.context==="object")return""+d+": "+e;e=d+(" "+(this.context||"=")+" ")+e;return a.level<=w?e:"("+e+")"},c.prototype.compilePatternMatch=function(a){var d,e,f,g,h,i,j,k,l,m,n,p,q,r,s,u,v,y,B,C,D,E,F,G,J,K,L;s=a.level===z,v=this.value,m=this.variable.base.objects;if(!(n=m.length)){f=v.compile(a);return a.level>=x?"("+f+")":f}i=this.variable.isObject();if(s&&n===1&&!((l=m[0])instanceof P)){l instanceof c?(D=l,E=D.variable,h=E.base,l=D.value):l.base instanceof H?(F=(new W(l.unwrapAll())).cacheReference(a),l=F[0],h=F[1]):h=i?l["this"]?l.properties[0].name:l:new A(0),d=o.test(h.unwrap().value||0),v=new W(v),v.properties.push(new(d?b:t)(h));if(G=l.unwrap().value,bm.call(I,G)>=0)throw new SyntaxError("assignment to a reserved word: "+l.compile(a)+" = "+v.compile(a));return(new c(l,v,null,{param:this.param})).compile(a,z)}y=v.compile(a,w),e=[],r=!1;if(!o.test(y)||this.variable.assigns(y))e.push(""+(p=a.scope.freeVariable("ref"))+" = "+y),y=p;for(g=B=0,C=m.length;B =0)throw new SyntaxError("assignment to a reserved word: "+l.compile(a)+" = "+u.compile(a));e.push((new c(l,u,null,{param:this.param,subpattern:!0})).compile(a,w))}!s&&!this.subpattern&&e.push(y),f=e.join(", ");return a.level =0&&(a.isExistentialEquals=!0);return(new F(this.context.slice(0,-1),b,new c(d,this.value,"="))).compile(a)},c.prototype.compileSplice=function(a){var b,c,d,e,f,g,h,i,j,k,l,m;k=this.variable.properties.pop().range,d=k.from,h=k.to,c=k.exclusive,g=this.variable.compile(a),l=(d!=null?d.cache(a,x):void 0)||["0","0"],e=l[0],f=l[1],h?(d!=null?d.isSimpleNumber():void 0)&&h.isSimpleNumber()?(h=+h.compile(a)- +f,c||(h+=1)):(h=h.compile(a,u)+" - "+f,c||(h+=" + 1")):h="9e9",m=this.value.cache(a,w),i=m[0],j=m[1],b="[].splice.apply("+g+", ["+e+", "+h+"].concat("+i+")), "+j;return a.level>z?"("+b+")":b};return c}(e),a.Code=j=function(a){function b(a,b,c){this.params=a||[],this.body=b||new f,this.bound=c==="boundfunc",this.bound&&(this.context="_this")}bl(b,a),b.prototype.children=["params","body"],b.prototype.isStatement=function(){return!!this.ctor},b.prototype.jumps=D,b.prototype.compileNode=function(a){var b,e,f,g,h,i,j,k,l,m,n,o,p,q,s,t,v,w,x,y,z,B,C,D,E,G,H,I,J,K,L,M,O;a.scope=new N(a.scope,this.body,this),a.scope.shared=$(a,"sharedScope"),a.indent+=R,delete a.bare,delete a.isExistentialEquals,l=[],e=[],H=this.paramNames();for(s=0,x=H.length;s =0)throw SyntaxError("multiple parameters named '"+i+"'");o.push(i)}!q&&!this.noReturn&&this.body.makeReturn(),this.bound&&(((O=a.scope.parent.method)!=null?O.bound:void 0)?this.bound=this.context=a.scope.parent.method.context:this["static"]||a.scope.parent.assign("_this","this")),g=a.indent,b="function",this.ctor&&(b+=" "+this.name),b+="("+l.join(", ")+") {",this.body.isEmpty()||(b+="\n"+this.body.compileWithDeclarations(a)+"\n"+this.tab),b+="}";if(this.ctor)return this.tab+b;return this.front||a.level>=u?"("+b+")":b},b.prototype.paramNames=function(){var a,b,c,d,e;a=[],e=this.params;for(c=0,d=e.length;c =0)throw SyntaxError('parameter name "'+a+'" is not allowed')}bl(b,a),b.prototype.children=["name","value"],b.prototype.compile=function(a){return this.name.compile(a,w)},b.prototype.asReference=function(a){var b;if(this.reference)return this.reference;b=this.name,b["this"]?(b=b.properties[0].name,b.value.reserved&&(b=new A(a.scope.freeVariable(b.value)))):b.isComplex()&&(b=new A(a.scope.freeVariable("arg"))),b=new W(b),this.splat&&(b=new P(b));return this.reference=b},b.prototype.isComplex=function(){return this.name.isComplex()},b.prototype.names=function(a){var b,c,e,f,g,h;a==null&&(a=this.name),b=function(a){var b;b=a.properties[0].name.value;return b.reserved?[]:[b]};if(a instanceof A)return[a.value];if(a instanceof W)return b(a);c=[],h=a.objects;for(f=0,g=h.length;f =c.length)return"";if(c.length===1){g=c[0].compile(a,w);if(d)return g;return""+bh("slice")+".call("+g+")"}e=c.slice(i);for(h=k=0,l=e.length;k 1?b.expressions.unshift(new r((new H(this.guard)).invert(),new A("continue"))):this.guard&&(b=f.wrap([new r(this.guard,b)]))),b="\n"+b.compile(a,z)+"\n"+this.tab),c=e+this.tab+("while ("+this.condition.compile(a,y)+") {"+b+"}"),this.returns&&(c+="\n"+this.tab+"return "+d+";");return c};return b}(e),a.Op=F=function(a){function e(a,c,d,e){if(a==="in")return new s(c,d);if(a==="do")return this.generateDo(c);if(a==="new"){if(c instanceof g&&!c["do"]&&!c.isNew)return c.newInstance();if(c instanceof j&&c.bound||c["do"])c=new H(c)}this.operator=b[a]||a,this.first=c,this.second=d,this.flip=!!e;return this}var b,c;bl(e,a),b={"==":"===","!=":"!==",of:"in"},c={"!==":"===","===":"!=="},e.prototype.children=["first","second"],e.prototype.isSimpleNumber=D,e.prototype.isUnary=function(){return!this.second},e.prototype.isComplex=function(){var a;return!this.isUnary()||(a=this.operator)!=="+"&&a!=="-"||this.first.isComplex()},e.prototype.isChainable=function(){var a;return(a=this.operator)==="<"||a===">"||a===">="||a==="<="||a==="==="||a==="!=="},e.prototype.invert=function(){var a,b,d,f,g;if(this.isChainable()&&this.first.isChainable()){a=!0,b=this;while(b&&b.operator)a&&(a=b.operator in c),b=b.first;if(!a)return(new H(this)).invert();b=this;while(b&&b.operator)b.invert=!b.invert,b.operator=c[b.operator],b=b.first;return this}if(f=c[this.operator]){this.operator=f,this.first.unwrap()instanceof e&&this.first.invert();return this}return this.second?(new H(this)).invert():this.operator==="!"&&(d=this.first.unwrap())instanceof e&&((g=d.operator)==="!"||g==="in"||g==="instanceof")?d:new e("!",this)},e.prototype.unfoldSoak=function(a){var b;return((b=this.operator)==="++"||b==="--"||b==="delete")&&bg(a,this,"first")},e.prototype.generateDo=function(a){var b,c,e,f,h,i,k,l;f=[],c=a instanceof d&&(h=a.value.unwrap())instanceof j?h:a,l=c.params||[];for(i=0,k=l.length;i =0))throw SyntaxError("prefix increment/decrement may not have eval or arguments operand");if(this.isUnary())return this.compileUnary(a);if(c)return this.compileChain(a);if(this.operator==="?")return this.compileExistence(a);b=this.first.compile(a,x)+" "+this.operator+" "+this.second.compile(a,x);return a.level<=x?b:"("+b+")"},e.prototype.compileChain=function(a){var b,c,d,e;e=this.first.second.cache(a),this.first.second=e[0],d=e[1],c=this.first.compile(a,x),b=""+c+" "+(this.invert?"&&":"||")+" "+d.compile(a)+" "+this.operator+" "+this.second.compile(a,x);return"("+b+")"},e.prototype.compileExistence=function(a){var b,c;this.first.isComplex()?(c=new A(a.scope.freeVariable("ref")),b=new H(new d(c,this.first))):(b=this.first,c=b);return(new r(new l(b),c,{type:"if"})).addElse(this.second).compile(a)},e.prototype.compileUnary=function(a){var b,c,d;if(a.level>=u)return(new H(this)).compile(a);c=[b=this.operator],d=b==="+"||b==="-",(b==="new"||b==="typeof"||b==="delete"||d&&this.first instanceof e&&this.first.operator===b)&&c.push(" ");if(d&&this.first instanceof e||b==="new"&&this.first.isStatement(a))this.first=new H(this.first);c.push(this.first.compile(a,x)),this.flip&&c.reverse();return c.join("")},e.prototype.toString=function(a){return e.__super__.toString.call(this,a,this.constructor.name+" "+this.operator)};return e}(e),a.In=s=function(a){function b(a,b){this.object=a,this.array=b}bl(b,a),b.prototype.children=["object","array"],b.prototype.invert=C,b.prototype.compileNode=function(a){var b,c,d,e,f;if(this.array instanceof W&&this.array.isArray()){f=this.array.base.objects;for(d=0,e=f.length;d = 0");if(d===c)return b;b=d+", "+b;return a.level =0)throw SyntaxError('catch variable may not be "'+this.error.value+'"');a.scope.check(this.error.value)||a.scope.add(this.error.value,"param");return" catch"+d+"{\n"+this.recovery.compile(a,z)+"\n"+this.tab+"}"}if(!this.ensure&&!this.recovery)return" catch (_error) {}"}.call(this),c=this.ensure?" finally {\n"+this.ensure.compile(a,z)+"\n"+this.tab+"}":"";return""+this.tab+"try {\n"+e+"\n"+this.tab+"}"+(b||"")+c};return b}(e),a.Throw=T=function(a){function b(a){this.expression=a}bl(b,a),b.prototype.children=["expression"],b.prototype.isStatement=Y,b.prototype.jumps=D,b.prototype.makeReturn=S,b.prototype.compileNode=function(a){return this.tab+("throw "+this.expression.compile(a)+";")};return b}(e),a.Existence=l=function(a){function b(a){this.expression=a}bl(b,a),b.prototype.children=["expression"],b.prototype.invert=C,b.prototype.compileNode=function(a){var b,c,d,e;this.expression.front=this.front,d=this.expression.compile(a,x),o.test(d)&&!a.scope.check(d)?(e=this.negated?["===","||"]:["!==","&&"],b=e[0],c=e[1],d="typeof "+d+" "+b+' "undefined" '+c+" "+d+" "+b+" null"):d=""+d+" "+(this.negated?"==":"!=")+" null";return a.level<=v?d:"("+d+")"};return b}(e),a.Parens=H=function(a){function b(a){this.body=a}bl(b,a),b.prototype.children=["body"],b.prototype.unwrap=function(){return this.body},b.prototype.isComplex=function(){return this.body.isComplex()},b.prototype.compileNode=function(a){var b,c,d;d=this.body.unwrap();if(d instanceof W&&d.isAtomic()){d.front=this.front;return d.compile(a)}c=d.compile(a,y),b=a.level 1?b.expressions.unshift(new r((new H(this.guard)).invert(),new A("continue"))):this.guard&&(b=f.wrap([new r(this.guard,b)]))),this.pattern&&b.expressions.unshift(new d(this.name,new A(""+F+"["+l+"]"))),c+=this.pluckDirectCall(a,b),s&&(G="\n"+i+s+";"),this.object&&(e=""+l+" in "+F,this.own&&(h="\n"+i+"if (!"+bh("hasProp")+".call("+F+", "+l+")) continue;")),b=b.compile(bd(a,{indent:i}),z),b&&(b="\n"+b+"\n");return""+c+(u||"")+this.tab+"for ("+e+") {"+h+G+b+this.tab+"}"+(v||"")},b.prototype.pluckDirectCall=function(a,b){var c,e,f,h,i,k,l,m,n,o,p,q,r,s,t;e="",o=b.expressions;for(i=m=0,n=o.length;m =v?"("+d+")":d},b.prototype.unfoldSoak=function(){return this.soak&&this};return b}(e),i={wrap:function(a,c,d){var e,h,i,k,l;if(a.jumps())return a;i=new j([],f.wrap([a])),e=[];if((k=a.contains(this.literalArgs))||a.contains(this.literalThis))l=new A(k?"apply":"call"),e=[new A("this")],k&&e.push(new A("arguments")),i=new W(i,[new b(l)]);i.noReturn=d,h=new g(i,e);return c?f.wrap([h]):h},literalArgs:function(a){return a instanceof A&&a.value==="arguments"&&!a.asKey},literalThis:function(a){return a instanceof A&&a.value==="this"&&!a.asKey||a instanceof j&&a.bound||a instanceof g&&a.isSuper}},bg=function(a,b,c){var d;if(!!(d=b[c].unfoldSoak(a))){b[c]=d.body,d.body=new W(b);return d}},V={"extends":function(){return"function(child, parent) { for (var key in parent) { if ("+bh("hasProp")+".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }"},bind:function(){return"function(fn, me){ return function(){ return fn.apply(me, arguments); }; }"},indexOf:function(){return"[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }"},hasProp:function(){return"{}.hasOwnProperty"},slice:function(){return"[].slice"}},z=1,y=2,w=3,v=4,x=5,u=6,R=" ",p="[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*",o=RegExp("^"+p+"$"),L=/^[+-]?\d+$/,B=RegExp("^(?:("+p+")\\.prototype(?:\\.("+p+")|\\[(\"(?:[^\\\\\"\\r\\n]|\\\\.)*\"|'(?:[^\\\\'\\r\\n]|\\\\.)*')\\]|\\[(0x[\\da-fA-F]+|\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\]))|("+p+")$"),q=/^['"]/,bh=function(a){var b;b="__"+a,N.root.assign(b,V[a]());return b},be=function(a,b){a=a.replace(/\n/g,"$&"+b);return a.replace(/\s+$/,"")}}).call(this)},require["./coffee-script"]=new function(){var a=this;(function(){var b,c,d,e,f,g,h,i,j,k=({}).hasOwnProperty;e=require("fs"),h=require("path"),j=require("./lexer"),b=j.Lexer,c=j.RESERVED,g=require("./parser").parser,i=require("vm"),require.extensions?require.extensions[".coffee"]=function(a,b){var c;c=d(e.readFileSync(b,"utf8"),{filename:b});return a._compile(c,b)}:require.registerExtension&&require.registerExtension(".coffee",function(a){return d(a)}),a.VERSION="1.3.3",a.RESERVED=c,a.helpers=require("./helpers"),a.compile=d=function(b,c){var d,e,h;c==null&&(c={}),h=a.helpers.merge;try{e=g.parse(f.tokenize(b)).compile(c);if(!c.header)return e}catch(i){c.filename&&(i.message="In "+c.filename+", "+i.message);throw i}d="Generated by CoffeeScript "+this.VERSION;return"// "+d+"\n"+e},a.tokens=function(a,b){return f.tokenize(a,b)},a.nodes=function(a,b){return typeof a=="string"?g.parse(f.tokenize(a,b)):g.parse(a)},a.run=function(a,b){var c;b==null&&(b={}),c=require.main,c.filename=process.argv[1]=b.filename?e.realpathSync(b.filename):".",c.moduleCache&&(c.moduleCache={}),c.paths=require("module")._nodeModulePaths(h.dirname(e.realpathSync(b.filename)));return h.extname(c.filename)!==".coffee"||require.extensions?c._compile(d(a,b),c.filename):c._compile(a,c.filename)},a.eval=function(a,b){var c,e,f,g,j,l,m,n,o,p,q,r,s,t;b==null&&(b={});if(!!(a=a.trim())){e=i.Script;if(e){if(b.sandbox!=null){if(b.sandbox instanceof e.createContext().constructor)m=b.sandbox;else{m=e.createContext(),r=b.sandbox;for(g in r){if(!k.call(r,g))continue;n=r[g],m[g]=n}}m.global=m.root=m.GLOBAL=m}else m=global;m.__filename=b.filename||"eval",m.__dirname=h.dirname(m.__filename);if(m===global&&!m.module&&!m.require){c=require("module"),m.module=q=new c(b.modulename||"eval"),m.require=t=function(a){return c._load(a,q,!0)},q.filename=m.__filename,s=Object.getOwnPropertyNames(require);for(o=0,p=s.length;o > Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); } }; self.reportSpecResults = function(spec) { reporterView.specComplete(spec); }; self.log = function() { var console = jasmine.getGlobal().console; if (console && console.log) { if (console.log.apply) { console.log.apply(console, arguments); } else { console.log(arguments); // ie fix: console.log.apply doesn't exist on ie } } }; self.specFilter = function(spec) { if (!focusedSpecName()) { return true; } return spec.getFullName().indexOf(focusedSpecName()) === 0; }; return self; function focusedSpecName() { var specName; (function memoizeFocusedSpec() { if (specName) { return; } var paramMap = []; var params = jasmine.HtmlReporter.parameters(doc); for (var i = 0; i < params.length; i++) { var p = params[i].split('='); paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); } specName = paramMap.spec; })(); return specName; } function createReporterDom(version) { dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, dom.banner = self.createDom('div', { className: 'banner' }, self.createDom('span', { className: 'title' }, "Jasmine "), self.createDom('span', { className: 'version' }, version)), dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), dom.alert = self.createDom('div', {className: 'alert'}, self.createDom('span', { className: 'exceptions' }, self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'), self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))), dom.results = self.createDom('div', {className: 'results'}, dom.summary = self.createDom('div', { className: 'summary' }), dom.details = self.createDom('div', { id: 'details' })) ); } function noTryCatch() { return window.location.search.match(/catch=false/); } function searchWithCatch() { var params = jasmine.HtmlReporter.parameters(window.document); var removed = false; var i = 0; while (!removed && i < params.length) { if (params[i].match(/catch=/)) { params.splice(i, 1); removed = true; } i++; } if (jasmine.CATCH_EXCEPTIONS) { params.push("catch=false"); } return params.join("&"); } function setExceptionHandling() { var chxCatch = document.getElementById('no_try_catch'); if (noTryCatch()) { chxCatch.setAttribute('checked', true); jasmine.CATCH_EXCEPTIONS = false; } chxCatch.onclick = function() { window.location.search = searchWithCatch(); }; } }; jasmine.HtmlReporter.parameters = function(doc) { var paramStr = doc.location.search.substring(1); var params = []; if (paramStr.length > 0) { params = paramStr.split('&'); } return params; } jasmine.HtmlReporter.sectionLink = function(sectionName) { var link = '?'; var params = []; if (sectionName) { params.push('spec=' + encodeURIComponent(sectionName)); } if (!jasmine.CATCH_EXCEPTIONS) { params.push("catch=false"); } if (params.length > 0) { link += params.join("&"); } return link; }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); jasmine.HtmlReporter.ReporterView = function(dom) { this.startedAt = new Date(); this.runningSpecCount = 0; this.completeSpecCount = 0; this.passedCount = 0; this.failedCount = 0; this.skippedCount = 0; this.createResultsMenu = function() { this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), ' | ', this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); this.summaryMenuItem.onclick = function() { dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); }; this.detailsMenuItem.onclick = function() { showDetails(); }; }; this.addSpecs = function(specs, specFilter) { this.totalSpecCount = specs.length; this.views = { specs: {}, suites: {} }; for (var i = 0; i < specs.length; i++) { var spec = specs[i]; this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); if (specFilter(spec)) { this.runningSpecCount++; } } }; this.specComplete = function(spec) { this.completeSpecCount++; if (isUndefined(this.views.specs[spec.id])) { this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); } var specView = this.views.specs[spec.id]; switch (specView.status()) { case 'passed': this.passedCount++; break; case 'failed': this.failedCount++; break; case 'skipped': this.skippedCount++; break; } specView.refresh(); this.refresh(); }; this.suiteComplete = function(suite) { var suiteView = this.views.suites[suite.id]; if (isUndefined(suiteView)) { return; } suiteView.refresh(); }; this.refresh = function() { if (isUndefined(this.resultsMenu)) { this.createResultsMenu(); } // currently running UI if (isUndefined(this.runningAlert)) { this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" }); dom.alert.appendChild(this.runningAlert); } this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); // skipped specs UI if (isUndefined(this.skippedAlert)) { this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" }); } this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; if (this.skippedCount === 1 && isDefined(dom.alert)) { dom.alert.appendChild(this.skippedAlert); } // passing specs UI if (isUndefined(this.passedAlert)) { this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" }); } this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); // failing specs UI if (isUndefined(this.failedAlert)) { this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); } this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); if (this.failedCount === 1 && isDefined(dom.alert)) { dom.alert.appendChild(this.failedAlert); dom.alert.appendChild(this.resultsMenu); } // summary info this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; }; this.complete = function() { dom.alert.removeChild(this.runningAlert); this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; if (this.failedCount === 0) { dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); } else { showDetails(); } dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); }; return this; function showDetails() { if (dom.reporter.className.search(/showDetails/) === -1) { dom.reporter.className += " showDetails"; } } function isUndefined(obj) { return typeof obj === 'undefined'; } function isDefined(obj) { return !isUndefined(obj); } function specPluralizedFor(count) { var str = count + " spec"; if (count > 1) { str += "s" } return str; } }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); jasmine.HtmlReporter.SpecView = function(spec, dom, views) { this.spec = spec; this.dom = dom; this.views = views; this.symbol = this.createDom('li', { className: 'pending' }); this.dom.symbolSummary.appendChild(this.symbol); this.summary = this.createDom('div', { className: 'specSummary' }, this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()), title: this.spec.getFullName() }, this.spec.description) ); this.detail = this.createDom('div', { className: 'specDetail' }, this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.spec.getFullName()), title: this.spec.getFullName() }, this.spec.getFullName()) ); }; jasmine.HtmlReporter.SpecView.prototype.status = function() { return this.getSpecStatus(this.spec); }; jasmine.HtmlReporter.SpecView.prototype.refresh = function() { this.symbol.className = this.status(); switch (this.status()) { case 'skipped': break; case 'passed': this.appendSummaryToSuiteDiv(); break; case 'failed': this.appendSummaryToSuiteDiv(); this.appendFailureDetail(); break; } }; jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { this.summary.className += ' ' + this.status(); this.appendToSummary(this.spec, this.summary); }; jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { this.detail.className += ' ' + this.status(); var resultItems = this.spec.results().getItems(); var messagesDiv = this.createDom('div', { className: 'messages' }); for (var i = 0; i < resultItems.length; i++) { var result = resultItems[i]; if (result.type == 'log') { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); } else if (result.type == 'expect' && result.passed && !result.passed()) { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); if (result.trace.stack) { messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); } } } if (messagesDiv.childNodes.length > 0) { this.detail.appendChild(messagesDiv); this.dom.details.appendChild(this.detail); } }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { this.suite = suite; this.dom = dom; this.views = views; this.element = this.createDom('div', { className: 'suite' }, this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description) ); this.appendToSummary(this.suite, this.element); }; jasmine.HtmlReporter.SuiteView.prototype.status = function() { return this.getSpecStatus(this.suite); }; jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { this.element.className += " " + this.status(); }; jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); /* @deprecated Use jasmine.HtmlReporter instead */ jasmine.TrivialReporter = function(doc) { this.document = doc || document; this.suiteDivs = {}; this.logRunningSpecs = false; }; jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { var el = document.createElement(type); for (var i = 2; i < arguments.length; i++) { var child = arguments[i]; if (typeof child === 'string') { el.appendChild(document.createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (var attr in attrs) { if (attr == "className") { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; }; jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { var showPassed, showSkipped; this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, this.createDom('div', { className: 'banner' }, this.createDom('div', { className: 'logo' }, this.createDom('span', { className: 'title' }, "Jasmine"), this.createDom('span', { className: 'version' }, runner.env.versionString())), this.createDom('div', { className: 'options' }, "Show ", showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") ) ), this.runnerDiv = this.createDom('div', { className: 'runner running' }, this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), this.runnerMessageSpan = this.createDom('span', {}, "Running..."), this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) ); this.document.body.appendChild(this.outerDiv); var suites = runner.suites(); for (var i = 0; i < suites.length; i++) { var suite = suites[i]; var suiteDiv = this.createDom('div', { className: 'suite' }, this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); this.suiteDivs[suite.id] = suiteDiv; var parentDiv = this.outerDiv; if (suite.parentSuite) { parentDiv = this.suiteDivs[suite.parentSuite.id]; } parentDiv.appendChild(suiteDiv); } this.startedAt = new Date(); var self = this; showPassed.onclick = function(evt) { if (showPassed.checked) { self.outerDiv.className += ' show-passed'; } else { self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); } }; showSkipped.onclick = function(evt) { if (showSkipped.checked) { self.outerDiv.className += ' show-skipped'; } else { self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); } }; }; jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { var results = runner.results(); var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; this.runnerDiv.setAttribute("class", className); //do it twice for IE this.runnerDiv.setAttribute("className", className); var specs = runner.specs(); var specCount = 0; for (var i = 0; i < specs.length; i++) { if (this.specFilter(specs[i])) { specCount++; } } var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); }; jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { var results = suite.results(); var status = results.passed() ? 'passed' : 'failed'; if (results.totalCount === 0) { // todo: change this to check results.skipped status = 'skipped'; } this.suiteDivs[suite.id].className += " " + status; }; jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { if (this.logRunningSpecs) { this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); } }; jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { var results = spec.results(); var status = results.passed() ? 'passed' : 'failed'; if (results.skipped) { status = 'skipped'; } var specDiv = this.createDom('div', { className: 'spec ' + status }, this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(spec.getFullName()), title: spec.getFullName() }, spec.description)); var resultItems = results.getItems(); var messagesDiv = this.createDom('div', { className: 'messages' }); for (var i = 0; i < resultItems.length; i++) { var result = resultItems[i]; if (result.type == 'log') { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); } else if (result.type == 'expect' && result.passed && !result.passed()) { messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); if (result.trace.stack) { messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); } } } if (messagesDiv.childNodes.length > 0) { specDiv.appendChild(messagesDiv); } this.suiteDivs[spec.suite.id].appendChild(specDiv); }; jasmine.TrivialReporter.prototype.log = function() { var console = jasmine.getGlobal().console; if (console && console.log) { if (console.log.apply) { console.log.apply(console, arguments); } else { console.log(arguments); // ie fix: console.log.apply doesn't exist on ie } } }; jasmine.TrivialReporter.prototype.getLocation = function() { return this.document.location; }; jasmine.TrivialReporter.prototype.specFilter = function(spec) { var paramMap = {}; var params = this.getLocation().search.substring(1).split('&'); for (var i = 0; i < params.length; i++) { var p = params[i].split('='); paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); } if (!paramMap.spec) { return true; } return spec.getFullName().indexOf(paramMap.spec) === 0; }; ================================================ FILE: test/lib/jasmine-jquery.js ================================================ /*! Jasmine-jQuery: a set of jQuery helpers for Jasmine tests. Version 1.7.0 https://github.com/velesin/jasmine-jquery Copyright (c) 2010-2013 Wojciech Zawistowski, Travis Jeffery 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. */ +function (jasmine, $) { "use strict"; jasmine.spiedEventsKey = function (selector, eventName) { return [$(selector).selector, eventName].toString() } jasmine.getFixtures = function () { return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures() } jasmine.getStyleFixtures = function () { return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures() } jasmine.Fixtures = function () { this.containerId = 'jasmine-fixtures' this.fixturesCache_ = {} this.fixturesPath = 'spec/javascripts/fixtures' } jasmine.Fixtures.prototype.set = function (html) { this.cleanUp() return this.createContainer_(html) } jasmine.Fixtures.prototype.appendSet= function (html) { this.addToContainer_(html) } jasmine.Fixtures.prototype.preload = function () { this.read.apply(this, arguments) } jasmine.Fixtures.prototype.load = function () { this.cleanUp() this.createContainer_(this.read.apply(this, arguments)) } jasmine.Fixtures.prototype.appendLoad = function () { this.addToContainer_(this.read.apply(this, arguments)) } jasmine.Fixtures.prototype.read = function () { var htmlChunks = [] , fixtureUrls = arguments for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex])) } return htmlChunks.join('') } jasmine.Fixtures.prototype.clearCache = function () { this.fixturesCache_ = {} } jasmine.Fixtures.prototype.cleanUp = function () { $('#' + this.containerId).remove() } jasmine.Fixtures.prototype.sandbox = function (attributes) { var attributesToSet = attributes || {} return $('
').attr(attributesToSet) } jasmine.Fixtures.prototype.createContainer_ = function (html) { var container = $('') .attr('id', this.containerId) .html(html) $(document.body).append(container) return container } jasmine.Fixtures.prototype.addToContainer_ = function (html){ var container = $(document.body).find('#'+this.containerId).append(html) if(!container.length){ this.createContainer_(html) } } jasmine.Fixtures.prototype.getFixtureHtml_ = function (url) { if (typeof this.fixturesCache_[url] === 'undefined') { this.loadFixtureIntoCache_(url) } return this.fixturesCache_[url] } jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { var self = this , url = this.makeFixtureUrl_(relativeUrl) , request = $.ajax({ async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded cache: false, url: url, success: function (data, status, $xhr) { self.fixturesCache_[relativeUrl] = $xhr.responseText }, error: function (jqXHR, status, errorThrown) { throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')') } }) } jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){ return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl } jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { return this[methodName].apply(this, passedArguments) } jasmine.StyleFixtures = function () { this.fixturesCache_ = {} this.fixturesNodes_ = [] this.fixturesPath = 'spec/javascripts/fixtures' } jasmine.StyleFixtures.prototype.set = function (css) { this.cleanUp() this.createStyle_(css) } jasmine.StyleFixtures.prototype.appendSet = function (css) { this.createStyle_(css) } jasmine.StyleFixtures.prototype.preload = function () { this.read_.apply(this, arguments) } jasmine.StyleFixtures.prototype.load = function () { this.cleanUp() this.createStyle_(this.read_.apply(this, arguments)) } jasmine.StyleFixtures.prototype.appendLoad = function () { this.createStyle_(this.read_.apply(this, arguments)) } jasmine.StyleFixtures.prototype.cleanUp = function () { while(this.fixturesNodes_.length) { this.fixturesNodes_.pop().remove() } } jasmine.StyleFixtures.prototype.createStyle_ = function (html) { var styleText = $('').html(html).text() , style = $('') this.fixturesNodes_.push(style) $('head').append(style) } jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_ jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_ jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_ jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_ jasmine.getJSONFixtures = function () { return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures() } jasmine.JSONFixtures = function () { this.fixturesCache_ = {} this.fixturesPath = 'spec/javascripts/fixtures/json' } jasmine.JSONFixtures.prototype.load = function () { this.read.apply(this, arguments) return this.fixturesCache_ } jasmine.JSONFixtures.prototype.read = function () { var fixtureUrls = arguments for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { this.getFixtureData_(fixtureUrls[urlIndex]) } return this.fixturesCache_ } jasmine.JSONFixtures.prototype.clearCache = function () { this.fixturesCache_ = {} } jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) { if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url) return this.fixturesCache_[url] } jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { var self = this , url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl $.ajax({ async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded cache: false, dataType: 'json', url: url, success: function (data) { self.fixturesCache_[relativeUrl] = data }, error: function (jqXHR, status, errorThrown) { throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')') } }) } jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { return this[methodName].apply(this, passedArguments) } jasmine.jQuery = function () {} jasmine.jQuery.browserTagCaseIndependentHtml = function (html) { return $('').append(html).html() } jasmine.jQuery.elementToString = function (element) { return $(element).map(function () { return this.outerHTML; }).toArray().join(', ') } jasmine.jQuery.matchersClass = {} var data = { spiedEvents: {} , handlers: [] } jasmine.jQuery.events = { spyOn: function (selector, eventName) { var handler = function (e) { data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = jasmine.util.argsToArray(arguments) } $(selector).on(eventName, handler) data.handlers.push(handler) return { selector: selector, eventName: eventName, handler: handler, reset: function (){ delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] } } }, args: function (selector, eventName) { var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] if (!actualArgs) { throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent." } return actualArgs }, wasTriggered: function (selector, eventName) { return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) }, wasTriggeredWith: function (selector, eventName, expectedArgs, env) { var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1) if (Object.prototype.toString.call(expectedArgs) !== '[object Array]') { actualArgs = actualArgs[0] } return env.equals_(expectedArgs, actualArgs) }, wasPrevented: function (selector, eventName) { var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] , e = args ? args[0] : undefined return e && e.isDefaultPrevented() }, wasStopped: function (selector, eventName) { var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] , e = args ? args[0] : undefined return e && e.isPropagationStopped() }, cleanUp: function () { data.spiedEvents = {} data.handlers = [] } } var jQueryMatchers = { toHaveClass: function (className) { return this.actual.hasClass(className) }, toHaveCss: function (css){ for (var prop in css){ var value = css[prop] // see issue #147 on gh ;if (value === 'auto' && this.actual.get(0).style[prop] === 'auto') continue if (this.actual.css(prop) !== value) return false } return true }, toBeVisible: function () { return this.actual.is(':visible') }, toBeHidden: function () { return this.actual.is(':hidden') }, toBeSelected: function () { return this.actual.is(':selected') }, toBeChecked: function () { return this.actual.is(':checked') }, toBeEmpty: function () { return this.actual.is(':empty') }, toBeInDOM: function () { return $.contains(document.documentElement, this.actual[0]) }, toExist: function () { return this.actual.length }, toHaveLength: function (length) { return this.actual.length === length }, toHaveAttr: function (attributeName, expectedAttributeValue) { return hasProperty(this.actual.attr(attributeName), expectedAttributeValue) }, toHaveProp: function (propertyName, expectedPropertyValue) { return hasProperty(this.actual.prop(propertyName), expectedPropertyValue) }, toHaveId: function (id) { return this.actual.attr('id') == id }, toHaveHtml: function (html) { return this.actual.html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) }, toContainHtml: function (html){ var actualHtml = this.actual.html() , expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html) return (actualHtml.indexOf(expectedHtml) >= 0) }, toHaveText: function (text) { var trimmedText = $.trim(this.actual.text()) if (text && $.isFunction(text.test)) { return text.test(trimmedText) } else { return trimmedText == text } }, toContainText: function (text) { var trimmedText = $.trim(this.actual.text()) if (text && $.isFunction(text.test)) { return text.test(trimmedText) } else { return trimmedText.indexOf(text) != -1 } }, toHaveValue: function (value) { return this.actual.val() === value }, toHaveData: function (key, expectedValue) { return hasProperty(this.actual.data(key), expectedValue) }, toBe: function (selector) { return this.actual.is(selector) }, toContain: function (selector) { return this.actual.find(selector).length }, toBeMatchedBy: function (selector) { return this.actual.filter(selector).length }, toBeDisabled: function (selector){ return this.actual.is(':disabled') }, toBeFocused: function (selector) { return this.actual[0] === this.actual[0].ownerDocument.activeElement }, toHandle: function (event) { var events = $._data(this.actual.get(0), "events") if(!events || !event || typeof event !== "string") { return false } var namespaces = event.split(".") , eventType = namespaces.shift() , sortedNamespaces = namespaces.slice(0).sort() , namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") if(events[eventType] && namespaces.length) { for(var i = 0; i < events[eventType].length; i++) { var namespace = events[eventType][i].namespace if(namespaceRegExp.test(namespace)) { return true } } } else { return events[eventType] && events[eventType].length > 0 } }, toHandleWith: function (eventName, eventHandler) { var normalizedEventName = eventName.split('.')[0] , stack = $._data(this.actual.get(0), "events")[normalizedEventName] for (var i = 0; i < stack.length; i++) { if (stack[i].handler == eventHandler) return true } return false } } var hasProperty = function (actualValue, expectedValue) { if (expectedValue === undefined) return actualValue !== undefined return actualValue === expectedValue } var bindMatcher = function (methodName) { var builtInMatcher = jasmine.Matchers.prototype[methodName] jasmine.jQuery.matchersClass[methodName] = function () { if (this.actual && (this.actual instanceof $ || jasmine.isDomNode(this.actual))) { this.actual = $(this.actual) var result = jQueryMatchers[methodName].apply(this, arguments) , element if (this.actual.get && (element = this.actual.get()[0]) && !$.isWindow(element) && element.tagName !== "HTML") this.actual = jasmine.jQuery.elementToString(this.actual) return result } if (builtInMatcher) { return builtInMatcher.apply(this, arguments) } return false } } for(var methodName in jQueryMatchers) { bindMatcher(methodName) } beforeEach(function () { this.addMatchers(jasmine.jQuery.matchersClass) this.addMatchers({ toHaveBeenTriggeredOn: function (selector) { this.message = function () { return [ "Expected event " + this.actual + " to have been triggered on " + selector, "Expected event " + this.actual + " not to have been triggered on " + selector ] } return jasmine.jQuery.events.wasTriggered(selector, this.actual) } }) this.addMatchers({ toHaveBeenTriggered: function (){ var eventName = this.actual.eventName , selector = this.actual.selector this.message = function () { return [ "Expected event " + eventName + " to have been triggered on " + selector, "Expected event " + eventName + " not to have been triggered on " + selector ] } return jasmine.jQuery.events.wasTriggered(selector, eventName) } }) this.addMatchers({ toHaveBeenTriggeredOnAndWith: function () { var selector = arguments[0] , expectedArgs = arguments[1] , wasTriggered = jasmine.jQuery.events.wasTriggered(selector, this.actual) this.message = function () { if (wasTriggered) { var actualArgs = jasmine.jQuery.events.args(selector, this.actual, expectedArgs)[1] return [ "Expected event " + this.actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs), "Expected event " + this.actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) ] } else { return [ "Expected event " + this.actual + " to have been triggered on " + selector, "Expected event " + this.actual + " not to have been triggered on " + selector ] } } return wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, this.actual, expectedArgs, this.env) } }) this.addMatchers({ toHaveBeenPreventedOn: function (selector) { this.message = function () { return [ "Expected event " + this.actual + " to have been prevented on " + selector, "Expected event " + this.actual + " not to have been prevented on " + selector ] } return jasmine.jQuery.events.wasPrevented(selector, this.actual) } }) this.addMatchers({ toHaveBeenPrevented: function () { var eventName = this.actual.eventName , selector = this.actual.selector this.message = function () { return [ "Expected event " + eventName + " to have been prevented on " + selector, "Expected event " + eventName + " not to have been prevented on " + selector ] } return jasmine.jQuery.events.wasPrevented(selector, eventName) } }) this.addMatchers({ toHaveBeenStoppedOn: function (selector) { this.message = function () { return [ "Expected event " + this.actual + " to have been stopped on " + selector, "Expected event " + this.actual + " not to have been stopped on " + selector ] } return jasmine.jQuery.events.wasStopped(selector, this.actual) } }) this.addMatchers({ toHaveBeenStopped: function () { var eventName = this.actual.eventName , selector = this.actual.selector this.message = function () { return [ "Expected event " + eventName + " to have been stopped on " + selector, "Expected event " + eventName + " not to have been stopped on " + selector ] } return jasmine.jQuery.events.wasStopped(selector, eventName) } }) jasmine.getEnv().addEqualityTester(function (a, b) { if(a instanceof $ && b instanceof $) { if(a.size() != b.size()) { return jasmine.undefined } else if(a.is(b)) { return true } } return jasmine.undefined }) }) afterEach(function () { jasmine.getFixtures().cleanUp() jasmine.getStyleFixtures().cleanUp() jasmine.jQuery.events.cleanUp() }) window.readFixtures = function () { return jasmine.getFixtures().proxyCallTo_('read', arguments) } window.preloadFixtures = function () { jasmine.getFixtures().proxyCallTo_('preload', arguments) } window.loadFixtures = function () { jasmine.getFixtures().proxyCallTo_('load', arguments) } window.appendLoadFixtures = function () { jasmine.getFixtures().proxyCallTo_('appendLoad', arguments) } window.setFixtures = function (html) { return jasmine.getFixtures().proxyCallTo_('set', arguments) } window.appendSetFixtures = function () { jasmine.getFixtures().proxyCallTo_('appendSet', arguments) } window.sandbox = function (attributes) { return jasmine.getFixtures().sandbox(attributes) } window.spyOnEvent = function (selector, eventName) { return jasmine.jQuery.events.spyOn(selector, eventName) } window.preloadStyleFixtures = function () { jasmine.getStyleFixtures().proxyCallTo_('preload', arguments) } window.loadStyleFixtures = function () { jasmine.getStyleFixtures().proxyCallTo_('load', arguments) } window.appendLoadStyleFixtures = function () { jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments) } window.setStyleFixtures = function (html) { jasmine.getStyleFixtures().proxyCallTo_('set', arguments) } window.appendSetStyleFixtures = function (html) { jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments) } window.loadJSONFixtures = function () { return jasmine.getJSONFixtures().proxyCallTo_('load', arguments) } window.getJSONFixture = function (url) { return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url] } }(window.jasmine, window.jQuery); ================================================ FILE: test/lib/jasmine.css ================================================ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } #HTMLReporter a { text-decoration: none; } #HTMLReporter a:hover { text-decoration: underline; } #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } #HTMLReporter #jasmine_content { position: fixed; right: 100%; } #HTMLReporter .version { color: #aaaaaa; } #HTMLReporter .banner { margin-top: 14px; } #HTMLReporter .duration { color: #aaaaaa; float: right; } #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } #HTMLReporter .symbolSummary li.passed { font-size: 14px; } #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } #HTMLReporter .symbolSummary li.failed { line-height: 9px; } #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } #HTMLReporter .symbolSummary li.pending { line-height: 11px; } #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } #HTMLReporter .runningAlert { background-color: #666666; } #HTMLReporter .skippedAlert { background-color: #aaaaaa; } #HTMLReporter .skippedAlert:first-child { background-color: #333333; } #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } #HTMLReporter .passingAlert { background-color: #a6b779; } #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } #HTMLReporter .failingAlert { background-color: #cf867e; } #HTMLReporter .failingAlert:first-child { background-color: #b03911; } #HTMLReporter .results { margin-top: 14px; } #HTMLReporter #details { display: none; } #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } #HTMLReporter.showDetails .summary { display: none; } #HTMLReporter.showDetails #details { display: block; } #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } #HTMLReporter .summary { margin-top: 14px; } #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } #HTMLReporter .summary .specSummary.failed a { color: #b03911; } #HTMLReporter .description + .suite { margin-top: 0; } #HTMLReporter .suite { margin-top: 14px; } #HTMLReporter .suite a { color: #333333; } #HTMLReporter #details .specDetail { margin-bottom: 28px; } #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } #HTMLReporter .resultMessage span.result { display: block; } #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } #TrivialReporter a:visited, #TrivialReporter a { color: #303; } #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } #TrivialReporter .runner.running { background-color: yellow; } #TrivialReporter .options { text-align: right; font-size: .8em; } #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } #TrivialReporter .suite .suite { margin: 5px; } #TrivialReporter .suite.passed { background-color: #dfd; } #TrivialReporter .suite.failed { background-color: #fdd; } #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } #TrivialReporter .spec.skipped { background-color: #bbb; } #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } #TrivialReporter .passed { background-color: #cfc; display: none; } #TrivialReporter .failed { background-color: #fbb; } #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } #TrivialReporter .resultMessage .mismatch { color: black; } #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } #TrivialReporter #jasmine_content { position: fixed; right: 100%; } #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } ================================================ FILE: test/lib/jasmine.js ================================================ var isCommonJS = typeof window == "undefined" && typeof exports == "object"; /** * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. * * @namespace */ var jasmine = {}; if (isCommonJS) exports.jasmine = jasmine; /** * @private */ jasmine.unimplementedMethod_ = function() { throw new Error("unimplemented method"); }; /** * Usejasmine.undefinedinstead ofundefined, sinceundefinedis just * a plain old variable and may be redefined by somebody else. * * @private */ jasmine.undefined = jasmine.___undefined___; /** * Show diagnostic messages in the console if set to true * */ jasmine.VERBOSE = false; /** * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. * */ jasmine.DEFAULT_UPDATE_INTERVAL = 250; /** * Maximum levels of nesting that will be included when an object is pretty-printed */ jasmine.MAX_PRETTY_PRINT_DEPTH = 40; /** * Default timeout interval in milliseconds for waitsFor() blocks. */ jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; /** * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. * Set to false to let the exception bubble up in the browser. * */ jasmine.CATCH_EXCEPTIONS = true; jasmine.getGlobal = function() { function getGlobal() { return this; } return getGlobal(); }; /** * Allows for bound functions to be compared. Internal use only. * * @ignore * @private * @param base {Object} bound 'this' for the function * @param name {Function} function to find */ jasmine.bindOriginal_ = function(base, name) { var original = base[name]; if (original.apply) { return function() { return original.apply(base, arguments); }; } else { // IE support return jasmine.getGlobal()[name]; } }; jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); jasmine.MessageResult = function(values) { this.type = 'log'; this.values = values; this.trace = new Error(); // todo: test better }; jasmine.MessageResult.prototype.toString = function() { var text = ""; for (var i = 0; i < this.values.length; i++) { if (i > 0) text += " "; if (jasmine.isString_(this.values[i])) { text += this.values[i]; } else { text += jasmine.pp(this.values[i]); } } return text; }; jasmine.ExpectationResult = function(params) { this.type = 'expect'; this.matcherName = params.matcherName; this.passed_ = params.passed; this.expected = params.expected; this.actual = params.actual; this.message = this.passed_ ? 'Passed.' : params.message; var trace = (params.trace || new Error(this.message)); this.trace = this.passed_ ? '' : trace; }; jasmine.ExpectationResult.prototype.toString = function () { return this.message; }; jasmine.ExpectationResult.prototype.passed = function () { return this.passed_; }; /** * Getter for the Jasmine environment. Ensures one gets created */ jasmine.getEnv = function() { var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); return env; }; /** * @ignore * @private * @param value * @returns {Boolean} */ jasmine.isArray_ = function(value) { return jasmine.isA_("Array", value); }; /** * @ignore * @private * @param value * @returns {Boolean} */ jasmine.isString_ = function(value) { return jasmine.isA_("String", value); }; /** * @ignore * @private * @param value * @returns {Boolean} */ jasmine.isNumber_ = function(value) { return jasmine.isA_("Number", value); }; /** * @ignore * @private * @param {String} typeName * @param value * @returns {Boolean} */ jasmine.isA_ = function(typeName, value) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; }; /** * Pretty printer for expecations. Takes any object and turns it into a human-readable string. * * @param value {Object} an object to be outputted * @returns {String} */ jasmine.pp = function(value) { var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); stringPrettyPrinter.format(value); return stringPrettyPrinter.string; }; /** * Returns true if the object is a DOM Node. * * @param {Object} obj object to check * @returns {Boolean} */ jasmine.isDomNode = function(obj) { return obj.nodeType > 0; }; /** * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. * * @example * // don't care about which function is passed in, as long as it's a function * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); * * @param {Class} clazz * @returns matchable object of the type clazz */ jasmine.any = function(clazz) { return new jasmine.Matchers.Any(clazz); }; /** * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the * attributes on the object. * * @example * // don't care about any other attributes than foo. * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); * * @param sample {Object} sample * @returns matchable object for the sample */ jasmine.objectContaining = function (sample) { return new jasmine.Matchers.ObjectContaining(sample); }; /** * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. * * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine * expectation syntax. Spies can be checked if they were called or not and what the calling params were. * * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). * * Spies are torn down at the end of every spec. * * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. * * @example * // a stub * var myStub = jasmine.createSpy('myStub'); // can be used anywhere * * // spy example * var foo = { * not: function(bool) { return !bool; } * } * * // actual foo.not will not be called, execution stops * spyOn(foo, 'not'); // foo.not spied upon, execution will continue to implementation * spyOn(foo, 'not').andCallThrough(); * * // fake example * var foo = { * not: function(bool) { return !bool; } * } * * // foo.not(val) will return val * spyOn(foo, 'not').andCallFake(function(value) {return value;}); * * // mock example * foo.not(7 == 7); * expect(foo.not).toHaveBeenCalled(); * expect(foo.not).toHaveBeenCalledWith(true); * * @constructor * @see spyOn, jasmine.createSpy, jasmine.createSpyObj * @param {String} name */ jasmine.Spy = function(name) { /** * The name of the spy, if provided. */ this.identity = name || 'unknown'; /** * Is this Object a spy? */ this.isSpy = true; /** * The actual function this spy stubs. */ this.plan = function() { }; /** * Tracking of the most recent call to the spy. * @example * var mySpy = jasmine.createSpy('foo'); * mySpy(1, 2); * mySpy.mostRecentCall.args = [1, 2]; */ this.mostRecentCall = {}; /** * Holds arguments for each call to the spy, indexed by call count * @example * var mySpy = jasmine.createSpy('foo'); * mySpy(1, 2); * mySpy(7, 8); * mySpy.mostRecentCall.args = [7, 8]; * mySpy.argsForCall[0] = [1, 2]; * mySpy.argsForCall[1] = [7, 8]; */ this.argsForCall = []; this.calls = []; }; /** * Tells a spy to call through to the actual implemenatation. * * @example * var foo = { * bar: function() { // do some stuff } * } * * // defining a spy on an existing property: foo.bar * spyOn(foo, 'bar').andCallThrough(); */ jasmine.Spy.prototype.andCallThrough = function() { this.plan = this.originalValue; return this; }; /** * For setting the return value of a spy. * * @example * // defining a spy from scratch: foo() returns 'baz' * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); * * // defining a spy on an existing property: foo.bar() returns 'baz' * spyOn(foo, 'bar').andReturn('baz'); * * @param {Object} value */ jasmine.Spy.prototype.andReturn = function(value) { this.plan = function() { return value; }; return this; }; /** * For throwing an exception when a spy is called. * * @example * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); * * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' * spyOn(foo, 'bar').andThrow('baz'); * * @param {String} exceptionMsg */ jasmine.Spy.prototype.andThrow = function(exceptionMsg) { this.plan = function() { throw exceptionMsg; }; return this; }; /** * Calls an alternate implementation when a spy is called. * * @example * var baz = function() { * // do some stuff, return something * } * // defining a spy from scratch: foo() calls the function baz * var foo = jasmine.createSpy('spy on foo').andCall(baz); * * // defining a spy on an existing property: foo.bar() calls an anonymnous function * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); * * @param {Function} fakeFunc */ jasmine.Spy.prototype.andCallFake = function(fakeFunc) { this.plan = fakeFunc; return this; }; /** * Resets all of a spy's the tracking variables so that it can be used again. * * @example * spyOn(foo, 'bar'); * * foo.bar(); * * expect(foo.bar.callCount).toEqual(1); * * foo.bar.reset(); * * expect(foo.bar.callCount).toEqual(0); */ jasmine.Spy.prototype.reset = function() { this.wasCalled = false; this.callCount = 0; this.argsForCall = []; this.calls = []; this.mostRecentCall = {}; }; jasmine.createSpy = function(name) { var spyObj = function() { spyObj.wasCalled = true; spyObj.callCount++; var args = jasmine.util.argsToArray(arguments); spyObj.mostRecentCall.object = this; spyObj.mostRecentCall.args = args; spyObj.argsForCall.push(args); spyObj.calls.push({object: this, args: args}); return spyObj.plan.apply(this, arguments); }; var spy = new jasmine.Spy(name); for (var prop in spy) { spyObj[prop] = spy[prop]; } spyObj.reset(); return spyObj; }; /** * Determines whether an object is a spy. * * @param {jasmine.Spy|Object} putativeSpy * @returns {Boolean} */ jasmine.isSpy = function(putativeSpy) { return putativeSpy && putativeSpy.isSpy; }; /** * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something * large in one call. * * @param {String} baseName name of spy class * @param {Array} methodNames array of names of methods to make spies */ jasmine.createSpyObj = function(baseName, methodNames) { if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); } var obj = {}; for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); } return obj; }; /** * All parameters are pretty-printed and concatenated together, then written to the current spec's output. * * Be careful not to leave calls tojasmine.login production code. */ jasmine.log = function() { var spec = jasmine.getEnv().currentSpec; spec.log.apply(spec, arguments); }; /** * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. * * @example * // spy example * var foo = { * not: function(bool) { return !bool; } * } * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops * * @see jasmine.createSpy * @param obj * @param methodName * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods */ var spyOn = function(obj, methodName) { return jasmine.getEnv().currentSpec.spyOn(obj, methodName); }; if (isCommonJS) exports.spyOn = spyOn; /** * Creates a Jasmine spec that will be added to the current suite. * * // TODO: pending tests * * @example * it('should be true', function() { * expect(true).toEqual(true); * }); * * @param {String} desc description of this specification * @param {Function} func defines the preconditions and expectations of the spec */ var it = function(desc, func) { return jasmine.getEnv().it(desc, func); }; if (isCommonJS) exports.it = it; /** * Creates a disabled Jasmine spec. * * A convenience method that allows existing specs to be disabled temporarily during development. * * @param {String} desc description of this specification * @param {Function} func defines the preconditions and expectations of the spec */ var xit = function(desc, func) { return jasmine.getEnv().xit(desc, func); }; if (isCommonJS) exports.xit = xit; /** * Starts a chain for a Jasmine expectation. * * It is passed an Object that is the actual value and should chain to one of the many * jasmine.Matchers functions. * * @param {Object} actual Actual value to test against and expected value * @return {jasmine.Matchers} */ var expect = function(actual) { return jasmine.getEnv().currentSpec.expect(actual); }; if (isCommonJS) exports.expect = expect; /** * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. * * @param {Function} func Function that defines part of a jasmine spec. */ var runs = function(func) { jasmine.getEnv().currentSpec.runs(func); }; if (isCommonJS) exports.runs = runs; /** * Waits a fixed time period before moving to the next block. * * @deprecated Use waitsFor() instead * @param {Number} timeout milliseconds to wait */ var waits = function(timeout) { jasmine.getEnv().currentSpec.waits(timeout); }; if (isCommonJS) exports.waits = waits; /** * Waits for the latchFunction to return true before proceeding to the next block. * * @param {Function} latchFunction * @param {String} optional_timeoutMessage * @param {Number} optional_timeout */ var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); }; if (isCommonJS) exports.waitsFor = waitsFor; /** * A function that is called before each spec in a suite. * * Used for spec setup, including validating assumptions. * * @param {Function} beforeEachFunction */ var beforeEach = function(beforeEachFunction) { jasmine.getEnv().beforeEach(beforeEachFunction); }; if (isCommonJS) exports.beforeEach = beforeEach; /** * A function that is called after each spec in a suite. * * Used for restoring any state that is hijacked during spec execution. * * @param {Function} afterEachFunction */ var afterEach = function(afterEachFunction) { jasmine.getEnv().afterEach(afterEachFunction); }; if (isCommonJS) exports.afterEach = afterEach; /** * Defines a suite of specifications. * * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization * of setup in some tests. * * @example * // TODO: a simple suite * * // TODO: a simple suite with a nested describe block * * @param {String} description A string, usually the class under test. * @param {Function} specDefinitions function that defines several specs. */ var describe = function(description, specDefinitions) { return jasmine.getEnv().describe(description, specDefinitions); }; if (isCommonJS) exports.describe = describe; /** * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. * * @param {String} description A string, usually the class under test. * @param {Function} specDefinitions function that defines several specs. */ var xdescribe = function(description, specDefinitions) { return jasmine.getEnv().xdescribe(description, specDefinitions); }; if (isCommonJS) exports.xdescribe = xdescribe; // Provide the XMLHttpRequest class for IE 5.x-6.x: jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { function tryIt(f) { try { return f(); } catch(e) { } return null; } var xhr = tryIt(function() { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }) || tryIt(function() { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }) || tryIt(function() { return new ActiveXObject("Msxml2.XMLHTTP"); }) || tryIt(function() { return new ActiveXObject("Microsoft.XMLHTTP"); }); if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); return xhr; } : XMLHttpRequest; /** * @namespace */ jasmine.util = {}; /** * Declare that a child class inherit it's prototype from the parent class. * * @private * @param {Function} childClass * @param {Function} parentClass */ jasmine.util.inherit = function(childClass, parentClass) { /** * @private */ var subclass = function() { }; subclass.prototype = parentClass.prototype; childClass.prototype = new subclass(); }; jasmine.util.formatException = function(e) { var lineNumber; if (e.line) { lineNumber = e.line; } else if (e.lineNumber) { lineNumber = e.lineNumber; } var file; if (e.sourceURL) { file = e.sourceURL; } else if (e.fileName) { file = e.fileName; } var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); if (file && lineNumber) { message += ' in ' + file + ' (line ' + lineNumber + ')'; } return message; }; jasmine.util.htmlEscape = function(str) { if (!str) return str; return str.replace(/&/g, '&') .replace(//g, '>'); }; jasmine.util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); return arrayOfArgs; }; jasmine.util.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; }; /** * Environment for Jasmine * * @constructor */ jasmine.Env = function() { this.currentSpec = null; this.currentSuite = null; this.currentRunner_ = new jasmine.Runner(this); this.reporter = new jasmine.MultiReporter(); this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; this.lastUpdate = 0; this.specFilter = function() { return true; }; this.nextSpecId_ = 0; this.nextSuiteId_ = 0; this.equalityTesters_ = []; // wrap matchers this.matchersClass = function() { jasmine.Matchers.apply(this, arguments); }; jasmine.util.inherit(this.matchersClass, jasmine.Matchers); jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); }; jasmine.Env.prototype.setTimeout = jasmine.setTimeout; jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; jasmine.Env.prototype.setInterval = jasmine.setInterval; jasmine.Env.prototype.clearInterval = jasmine.clearInterval; /** * @returns an object containing jasmine version build info, if set. */ jasmine.Env.prototype.version = function () { if (jasmine.version_) { return jasmine.version_; } else { throw new Error('Version not set'); } }; /** * @returns string containing jasmine version build info, if set. */ jasmine.Env.prototype.versionString = function() { if (!jasmine.version_) { return "version unknown"; } var version = this.version(); var versionString = version.major + "." + version.minor + "." + version.build; if (version.release_candidate) { versionString += ".rc" + version.release_candidate; } versionString += " revision " + version.revision; return versionString; }; /** * @returns a sequential integer starting at 0 */ jasmine.Env.prototype.nextSpecId = function () { return this.nextSpecId_++; }; /** * @returns a sequential integer starting at 0 */ jasmine.Env.prototype.nextSuiteId = function () { return this.nextSuiteId_++; }; /** * Register a reporter to receive status updates from Jasmine. * @param {jasmine.Reporter} reporter An object which will receive status updates. */ jasmine.Env.prototype.addReporter = function(reporter) { this.reporter.addReporter(reporter); }; jasmine.Env.prototype.execute = function() { this.currentRunner_.execute(); }; jasmine.Env.prototype.describe = function(description, specDefinitions) { var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); var parentSuite = this.currentSuite; if (parentSuite) { parentSuite.add(suite); } else { this.currentRunner_.add(suite); } this.currentSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch(e) { declarationError = e; } if (declarationError) { this.it("encountered a declaration exception", function() { throw declarationError; }); } this.currentSuite = parentSuite; return suite; }; jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { if (this.currentSuite) { this.currentSuite.beforeEach(beforeEachFunction); } else { this.currentRunner_.beforeEach(beforeEachFunction); } }; jasmine.Env.prototype.currentRunner = function () { return this.currentRunner_; }; jasmine.Env.prototype.afterEach = function(afterEachFunction) { if (this.currentSuite) { this.currentSuite.afterEach(afterEachFunction); } else { this.currentRunner_.afterEach(afterEachFunction); } }; jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { return { execute: function() { } }; }; jasmine.Env.prototype.it = function(description, func) { var spec = new jasmine.Spec(this, this.currentSuite, description); this.currentSuite.add(spec); this.currentSpec = spec; if (func) { spec.runs(func); } return spec; }; jasmine.Env.prototype.xit = function(desc, func) { return { id: this.nextSpecId(), runs: function() { } }; }; jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { if (a.source != b.source) mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); if (a.ignoreCase != b.ignoreCase) mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); if (a.global != b.global) mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); if (a.multiline != b.multiline) mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); if (a.sticky != b.sticky) mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); return (mismatchValues.length === 0); }; jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { return true; } a.__Jasmine_been_here_before__ = b; b.__Jasmine_been_here_before__ = a; var hasKey = function(obj, keyName) { return obj !== null && obj[keyName] !== jasmine.undefined; }; for (var property in b) { if (!hasKey(a, property) && hasKey(b, property)) { mismatchKeys.push("expected has key '" + property + "', but missing from actual."); } } for (property in a) { if (!hasKey(b, property) && hasKey(a, property)) { mismatchKeys.push("expected missing key '" + property + "', but present in actual."); } } for (property in b) { if (property == '__Jasmine_been_here_before__') continue; if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); } } if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { mismatchValues.push("arrays were not the same length"); } delete a.__Jasmine_been_here_before__; delete b.__Jasmine_been_here_before__; return (mismatchKeys.length === 0 && mismatchValues.length === 0); }; jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { mismatchKeys = mismatchKeys || []; mismatchValues = mismatchValues || []; for (var i = 0; i < this.equalityTesters_.length; i++) { var equalityTester = this.equalityTesters_[i]; var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); if (result !== jasmine.undefined) return result; } if (a === b) return true; if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { return (a == jasmine.undefined && b == jasmine.undefined); } if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { return a === b; } if (a instanceof Date && b instanceof Date) { return a.getTime() == b.getTime(); } if (a.jasmineMatches) { return a.jasmineMatches(b); } if (b.jasmineMatches) { return b.jasmineMatches(a); } if (a instanceof jasmine.Matchers.ObjectContaining) { return a.matches(b); } if (b instanceof jasmine.Matchers.ObjectContaining) { return b.matches(a); } if (jasmine.isString_(a) && jasmine.isString_(b)) { return (a == b); } if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { return (a == b); } if (a instanceof RegExp && b instanceof RegExp) { return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); } if (typeof a === "object" && typeof b === "object") { return this.compareObjects_(a, b, mismatchKeys, mismatchValues); } //Straight check return (a === b); }; jasmine.Env.prototype.contains_ = function(haystack, needle) { if (jasmine.isArray_(haystack)) { for (var i = 0; i < haystack.length; i++) { if (this.equals_(haystack[i], needle)) return true; } return false; } return haystack.indexOf(needle) >= 0; }; jasmine.Env.prototype.addEqualityTester = function(equalityTester) { this.equalityTesters_.push(equalityTester); }; /** No-op base class for Jasmine reporters. * * @constructor */ jasmine.Reporter = function() { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportRunnerResults = function(runner) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportSuiteResults = function(suite) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportSpecStarting = function(spec) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.reportSpecResults = function(spec) { }; //noinspection JSUnusedLocalSymbols jasmine.Reporter.prototype.log = function(str) { }; /** * Blocks are functions with executable code that make up a spec. * * @constructor * @param {jasmine.Env} env * @param {Function} func * @param {jasmine.Spec} spec */ jasmine.Block = function(env, func, spec) { this.env = env; this.func = func; this.spec = spec; }; jasmine.Block.prototype.execute = function(onComplete) { if (!jasmine.CATCH_EXCEPTIONS) { this.func.apply(this.spec); } else { try { this.func.apply(this.spec); } catch (e) { this.spec.fail(e); } } onComplete(); }; /** JavaScript API reporter. * * @constructor */ jasmine.JsApiReporter = function() { this.started = false; this.finished = false; this.suites_ = []; this.results_ = {}; }; jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { this.started = true; var suites = runner.topLevelSuites(); for (var i = 0; i < suites.length; i++) { var suite = suites[i]; this.suites_.push(this.summarize_(suite)); } }; jasmine.JsApiReporter.prototype.suites = function() { return this.suites_; }; jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { var isSuite = suiteOrSpec instanceof jasmine.Suite; var summary = { id: suiteOrSpec.id, name: suiteOrSpec.description, type: isSuite ? 'suite' : 'spec', children: [] }; if (isSuite) { var children = suiteOrSpec.children(); for (var i = 0; i < children.length; i++) { summary.children.push(this.summarize_(children[i])); } } return summary; }; jasmine.JsApiReporter.prototype.results = function() { return this.results_; }; jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { return this.results_[specId]; }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { this.finished = true; }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { this.results_[spec.id] = { messages: spec.results().getItems(), result: spec.results().failedCount > 0 ? "failed" : "passed" }; }; //noinspection JSUnusedLocalSymbols jasmine.JsApiReporter.prototype.log = function(str) { }; jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ var results = {}; for (var i = 0; i < specIds.length; i++) { var specId = specIds[i]; results[specId] = this.summarizeResult_(this.results_[specId]); } return results; }; jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ var summaryMessages = []; var messagesLength = result.messages.length; for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { var resultMessage = result.messages[messageIndex]; summaryMessages.push({ text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, passed: resultMessage.passed ? resultMessage.passed() : true, type: resultMessage.type, message: resultMessage.message, trace: { stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined } }); } return { result : result.result, messages : summaryMessages }; }; /** * @constructor * @param {jasmine.Env} env * @param actual * @param {jasmine.Spec} spec */ jasmine.Matchers = function(env, actual, spec, opt_isNot) { this.env = env; this.actual = actual; this.spec = spec; this.isNot = opt_isNot || false; this.reportWasCalled_ = false; }; // todo: @deprecated as of Jasmine 0.11, remove soon [xw] jasmine.Matchers.pp = function(str) { throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); }; // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] jasmine.Matchers.prototype.report = function(result, failing_message, details) { throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); }; jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { for (var methodName in prototype) { if (methodName == 'report') continue; var orig = prototype[methodName]; matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); } }; jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { return function() { var matcherArgs = jasmine.util.argsToArray(arguments); var result = matcherFunction.apply(this, arguments); if (this.isNot) { result = !result; } if (this.reportWasCalled_) return result; var message; if (!result) { if (this.message) { message = this.message.apply(this, arguments); if (jasmine.isArray_(message)) { message = message[this.isNot ? 1 : 0]; } } else { var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; if (matcherArgs.length > 0) { for (var i = 0; i < matcherArgs.length; i++) { if (i > 0) message += ","; message += " " + jasmine.pp(matcherArgs[i]); } } message += "."; } } var expectationResult = new jasmine.ExpectationResult({ matcherName: matcherName, passed: result, expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], actual: this.actual, message: message }); this.spec.addMatcherResult(expectationResult); return jasmine.undefined; }; }; /** * toBe: compares the actual to the expected using === * @param expected */ jasmine.Matchers.prototype.toBe = function(expected) { return this.actual === expected; }; /** * toNotBe: compares the actual to the expected using !== * @param expected * @deprecated as of 1.0. Use not.toBe() instead. */ jasmine.Matchers.prototype.toNotBe = function(expected) { return this.actual !== expected; }; /** * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. * * @param expected */ jasmine.Matchers.prototype.toEqual = function(expected) { return this.env.equals_(this.actual, expected); }; /** * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual * @param expected * @deprecated as of 1.0. Use not.toEqual() instead. */ jasmine.Matchers.prototype.toNotEqual = function(expected) { return !this.env.equals_(this.actual, expected); }; /** * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes * a pattern or a String. * * @param expected */ jasmine.Matchers.prototype.toMatch = function(expected) { return new RegExp(expected).test(this.actual); }; /** * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch * @param expected * @deprecated as of 1.0. Use not.toMatch() instead. */ jasmine.Matchers.prototype.toNotMatch = function(expected) { return !(new RegExp(expected).test(this.actual)); }; /** * Matcher that compares the actual to jasmine.undefined. */ jasmine.Matchers.prototype.toBeDefined = function() { return (this.actual !== jasmine.undefined); }; /** * Matcher that compares the actual to jasmine.undefined. */ jasmine.Matchers.prototype.toBeUndefined = function() { return (this.actual === jasmine.undefined); }; /** * Matcher that compares the actual to null. */ jasmine.Matchers.prototype.toBeNull = function() { return (this.actual === null); }; /** * Matcher that compares the actual to NaN. */ jasmine.Matchers.prototype.toBeNaN = function() { this.message = function() { return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; }; return (this.actual !== this.actual); }; /** * Matcher that boolean not-nots the actual. */ jasmine.Matchers.prototype.toBeTruthy = function() { return !!this.actual; }; /** * Matcher that boolean nots the actual. */ jasmine.Matchers.prototype.toBeFalsy = function() { return !this.actual; }; /** * Matcher that checks to see if the actual, a Jasmine spy, was called. */ jasmine.Matchers.prototype.toHaveBeenCalled = function() { if (arguments.length > 0) { throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); } if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { return [ "Expected spy " + this.actual.identity + " to have been called.", "Expected spy " + this.actual.identity + " not to have been called." ]; }; return this.actual.wasCalled; }; /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; /** * Matcher that checks to see if the actual, a Jasmine spy, was not called. * * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead */ jasmine.Matchers.prototype.wasNotCalled = function() { if (arguments.length > 0) { throw new Error('wasNotCalled does not take arguments'); } if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { return [ "Expected spy " + this.actual.identity + " to not have been called.", "Expected spy " + this.actual.identity + " to have been called." ]; }; return !this.actual.wasCalled; }; /** * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. * * @example * */ jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; var positiveMessage = ""; if (this.actual.callCount === 0) { positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; } else { positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') } return [positiveMessage, invertedMessage]; }; return this.env.contains_(this.actual.argsForCall, expectedArgs); }; /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ jasmine.Matchers.prototype.wasNotCalledWith = function() { var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); } this.message = function() { return [ "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" ]; }; return !this.env.contains_(this.actual.argsForCall, expectedArgs); }; /** * Matcher that checks that the expected item is an element in the actual Array. * * @param {Object} expected */ jasmine.Matchers.prototype.toContain = function(expected) { return this.env.contains_(this.actual, expected); }; /** * Matcher that checks that the expected item is NOT an element in the actual Array. * * @param {Object} expected * @deprecated as of 1.0. Use not.toContain() instead. */ jasmine.Matchers.prototype.toNotContain = function(expected) { return !this.env.contains_(this.actual, expected); }; jasmine.Matchers.prototype.toBeLessThan = function(expected) { return this.actual < expected; }; jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { return this.actual > expected; }; /** * Matcher that checks that the expected item is equal to the actual item * up to a given level of decimal precision (default 2). * * @param {Number} expected * @param {Number} precision, as number of decimal places */ jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { if (!(precision === 0)) { precision = precision || 2; } return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); }; /** * Matcher that checks that the expected exception was thrown by the actual. * * @param {String} [expected] */ jasmine.Matchers.prototype.toThrow = function(expected) { var result = false; var exception; if (typeof this.actual != 'function') { throw new Error('Actual is not a function'); } try { this.actual(); } catch (e) { exception = e; } if (exception) { result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); } var not = this.isNot ? "not " : ""; this.message = function() { if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); } else { return "Expected function to throw an exception."; } }; return result; }; jasmine.Matchers.Any = function(expectedClass) { this.expectedClass = expectedClass; }; jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { if (this.expectedClass == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedClass == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedClass == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedClass == Object) { return typeof other == 'object'; } return other instanceof this.expectedClass; }; jasmine.Matchers.Any.prototype.jasmineToString = function() { return ''; }; jasmine.Matchers.ObjectContaining = function (sample) { this.sample = sample; }; jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { mismatchKeys = mismatchKeys || []; mismatchValues = mismatchValues || []; var env = jasmine.getEnv(); var hasKey = function(obj, keyName) { return obj != null && obj[keyName] !== jasmine.undefined; }; for (var property in this.sample) { if (!hasKey(other, property) && hasKey(this.sample, property)) { mismatchKeys.push("expected has key '" + property + "', but missing from actual."); } else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); } } return (mismatchKeys.length === 0 && mismatchValues.length === 0); }; jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { return " "; }; // Mock setTimeout, clearTimeout // Contributed by Pivotal Computer Systems, www.pivotalsf.com jasmine.FakeTimer = function() { this.reset(); var self = this; self.setTimeout = function(funcToCall, millis) { self.timeoutsMade++; self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); return self.timeoutsMade; }; self.setInterval = function(funcToCall, millis) { self.timeoutsMade++; self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); return self.timeoutsMade; }; self.clearTimeout = function(timeoutKey) { self.scheduledFunctions[timeoutKey] = jasmine.undefined; }; self.clearInterval = function(timeoutKey) { self.scheduledFunctions[timeoutKey] = jasmine.undefined; }; }; jasmine.FakeTimer.prototype.reset = function() { this.timeoutsMade = 0; this.scheduledFunctions = {}; this.nowMillis = 0; }; jasmine.FakeTimer.prototype.tick = function(millis) { var oldMillis = this.nowMillis; var newMillis = oldMillis + millis; this.runFunctionsWithinRange(oldMillis, newMillis); this.nowMillis = newMillis; }; jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { var scheduledFunc; var funcsToRun = []; for (var timeoutKey in this.scheduledFunctions) { scheduledFunc = this.scheduledFunctions[timeoutKey]; if (scheduledFunc != jasmine.undefined && scheduledFunc.runAtMillis >= oldMillis && scheduledFunc.runAtMillis <= nowMillis) { funcsToRun.push(scheduledFunc); this.scheduledFunctions[timeoutKey] = jasmine.undefined; } } if (funcsToRun.length > 0) { funcsToRun.sort(function(a, b) { return a.runAtMillis - b.runAtMillis; }); for (var i = 0; i < funcsToRun.length; ++i) { try { var funcToRun = funcsToRun[i]; this.nowMillis = funcToRun.runAtMillis; funcToRun.funcToCall(); if (funcToRun.recurring) { this.scheduleFunction(funcToRun.timeoutKey, funcToRun.funcToCall, funcToRun.millis, true); } } catch(e) { } } this.runFunctionsWithinRange(oldMillis, nowMillis); } }; jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { this.scheduledFunctions[timeoutKey] = { runAtMillis: this.nowMillis + millis, funcToCall: funcToCall, recurring: recurring, timeoutKey: timeoutKey, millis: millis }; }; /** * @namespace */ jasmine.Clock = { defaultFakeTimer: new jasmine.FakeTimer(), reset: function() { jasmine.Clock.assertInstalled(); jasmine.Clock.defaultFakeTimer.reset(); }, tick: function(millis) { jasmine.Clock.assertInstalled(); jasmine.Clock.defaultFakeTimer.tick(millis); }, runFunctionsWithinRange: function(oldMillis, nowMillis) { jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); }, scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); }, useMock: function() { if (!jasmine.Clock.isInstalled()) { var spec = jasmine.getEnv().currentSpec; spec.after(jasmine.Clock.uninstallMock); jasmine.Clock.installMock(); } }, installMock: function() { jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; }, uninstallMock: function() { jasmine.Clock.assertInstalled(); jasmine.Clock.installed = jasmine.Clock.real; }, real: { setTimeout: jasmine.getGlobal().setTimeout, clearTimeout: jasmine.getGlobal().clearTimeout, setInterval: jasmine.getGlobal().setInterval, clearInterval: jasmine.getGlobal().clearInterval }, assertInstalled: function() { if (!jasmine.Clock.isInstalled()) { throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); } }, isInstalled: function() { return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; }, installed: null }; jasmine.Clock.installed = jasmine.Clock.real; //else for IE support jasmine.getGlobal().setTimeout = function(funcToCall, millis) { if (jasmine.Clock.installed.setTimeout.apply) { return jasmine.Clock.installed.setTimeout.apply(this, arguments); } else { return jasmine.Clock.installed.setTimeout(funcToCall, millis); } }; jasmine.getGlobal().setInterval = function(funcToCall, millis) { if (jasmine.Clock.installed.setInterval.apply) { return jasmine.Clock.installed.setInterval.apply(this, arguments); } else { return jasmine.Clock.installed.setInterval(funcToCall, millis); } }; jasmine.getGlobal().clearTimeout = function(timeoutKey) { if (jasmine.Clock.installed.clearTimeout.apply) { return jasmine.Clock.installed.clearTimeout.apply(this, arguments); } else { return jasmine.Clock.installed.clearTimeout(timeoutKey); } }; jasmine.getGlobal().clearInterval = function(timeoutKey) { if (jasmine.Clock.installed.clearTimeout.apply) { return jasmine.Clock.installed.clearInterval.apply(this, arguments); } else { return jasmine.Clock.installed.clearInterval(timeoutKey); } }; /** * @constructor */ jasmine.MultiReporter = function() { this.subReporters_ = []; }; jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); jasmine.MultiReporter.prototype.addReporter = function(reporter) { this.subReporters_.push(reporter); }; (function() { var functionNames = [ "reportRunnerStarting", "reportRunnerResults", "reportSuiteResults", "reportSpecStarting", "reportSpecResults", "log" ]; for (var i = 0; i < functionNames.length; i++) { var functionName = functionNames[i]; jasmine.MultiReporter.prototype[functionName] = (function(functionName) { return function() { for (var j = 0; j < this.subReporters_.length; j++) { var subReporter = this.subReporters_[j]; if (subReporter[functionName]) { subReporter[functionName].apply(subReporter, arguments); } } }; })(functionName); } })(); /** * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults * * @constructor */ jasmine.NestedResults = function() { /** * The total count of results */ this.totalCount = 0; /** * Number of passed results */ this.passedCount = 0; /** * Number of failed results */ this.failedCount = 0; /** * Was this suite/spec skipped? */ this.skipped = false; /** * @ignore */ this.items_ = []; }; /** * Roll up the result counts. * * @param result */ jasmine.NestedResults.prototype.rollupCounts = function(result) { this.totalCount += result.totalCount; this.passedCount += result.passedCount; this.failedCount += result.failedCount; }; /** * Adds a log message. * @param values Array of message parts which will be concatenated later. */ jasmine.NestedResults.prototype.log = function(values) { this.items_.push(new jasmine.MessageResult(values)); }; /** * Getter for the results: message & results. */ jasmine.NestedResults.prototype.getItems = function() { return this.items_; }; /** * Adds a result, tracking counts (total, passed, & failed) * @param {jasmine.ExpectationResult|jasmine.NestedResults} result */ jasmine.NestedResults.prototype.addResult = function(result) { if (result.type != 'log') { if (result.items_) { this.rollupCounts(result); } else { this.totalCount++; if (result.passed()) { this.passedCount++; } else { this.failedCount++; } } } this.items_.push(result); }; /** * @returns {Boolean} True if everything below passed */ jasmine.NestedResults.prototype.passed = function() { return this.passedCount === this.totalCount; }; /** * Base class for pretty printing for expectation results. */ jasmine.PrettyPrinter = function() { this.ppNestLevel_ = 0; }; /** * Formats a value in a nice, human-readable string. * * @param value */ jasmine.PrettyPrinter.prototype.format = function(value) { this.ppNestLevel_++; try { if (value === jasmine.undefined) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === jasmine.getGlobal()) { this.emitScalar(' '); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString()); } else if (typeof value === 'string') { this.emitString(value); } else if (jasmine.isSpy(value)) { this.emitScalar("spy on " + value.identity); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (typeof value.nodeType === 'number') { this.emitScalar('HTMLNode'); } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (value.__Jasmine_been_here_before__) { this.emitScalar(' '); } else if (jasmine.isArray_(value) || typeof value == 'object') { value.__Jasmine_been_here_before__ = true; if (jasmine.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } delete value.__Jasmine_been_here_before__; } else { this.emitScalar(value.toString()); } } finally { this.ppNestLevel_--; } }; jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!obj.hasOwnProperty(property)) continue; if (property == '__Jasmine_been_here_before__') continue; fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && obj.__lookupGetter__(property) !== null) : false); } }; jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; jasmine.StringPrettyPrinter = function() { jasmine.PrettyPrinter.call(this); this.string = ''; }; jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { this.append(value); }; jasmine.StringPrettyPrinter.prototype.emitString = function(value) { this.append("'" + value + "'"); }; jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { this.append("Array"); return; } this.append('[ '); for (var i = 0; i < array.length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } this.append(' ]'); }; jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { this.append("Object"); return; } var self = this; this.append('{ '); var first = true; this.iterateObject(obj, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.append(property); self.append(' : '); if (isGetter) { self.append(' '); } else { self.format(obj[property]); } }); this.append(' }'); }; jasmine.StringPrettyPrinter.prototype.append = function(value) { this.string += value; }; jasmine.Queue = function(env) { this.env = env; // parallel to blocks. each true value in this array means the block will // get executed even if we abort this.ensured = []; this.blocks = []; this.running = false; this.index = 0; this.offset = 0; this.abort = false; }; jasmine.Queue.prototype.addBefore = function(block, ensure) { if (ensure === jasmine.undefined) { ensure = false; } this.blocks.unshift(block); this.ensured.unshift(ensure); }; jasmine.Queue.prototype.add = function(block, ensure) { if (ensure === jasmine.undefined) { ensure = false; } this.blocks.push(block); this.ensured.push(ensure); }; jasmine.Queue.prototype.insertNext = function(block, ensure) { if (ensure === jasmine.undefined) { ensure = false; } this.ensured.splice((this.index + this.offset + 1), 0, ensure); this.blocks.splice((this.index + this.offset + 1), 0, block); this.offset++; }; jasmine.Queue.prototype.start = function(onComplete) { this.running = true; this.onComplete = onComplete; this.next_(); }; jasmine.Queue.prototype.isRunning = function() { return this.running; }; jasmine.Queue.LOOP_DONT_RECURSE = true; jasmine.Queue.prototype.next_ = function() { var self = this; var goAgain = true; while (goAgain) { goAgain = false; if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { var calledSynchronously = true; var completedSynchronously = false; var onComplete = function () { if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { completedSynchronously = true; return; } if (self.blocks[self.index].abort) { self.abort = true; } self.offset = 0; self.index++; var now = new Date().getTime(); if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { self.env.lastUpdate = now; self.env.setTimeout(function() { self.next_(); }, 0); } else { if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { goAgain = true; } else { self.next_(); } } }; self.blocks[self.index].execute(onComplete); calledSynchronously = false; if (completedSynchronously) { onComplete(); } } else { self.running = false; if (self.onComplete) { self.onComplete(); } } } }; jasmine.Queue.prototype.results = function() { var results = new jasmine.NestedResults(); for (var i = 0; i < this.blocks.length; i++) { if (this.blocks[i].results) { results.addResult(this.blocks[i].results()); } } return results; }; /** * Runner * * @constructor * @param {jasmine.Env} env */ jasmine.Runner = function(env) { var self = this; self.env = env; self.queue = new jasmine.Queue(env); self.before_ = []; self.after_ = []; self.suites_ = []; }; jasmine.Runner.prototype.execute = function() { var self = this; if (self.env.reporter.reportRunnerStarting) { self.env.reporter.reportRunnerStarting(this); } self.queue.start(function () { self.finishCallback(); }); }; jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { beforeEachFunction.typeName = 'beforeEach'; this.before_.splice(0,0,beforeEachFunction); }; jasmine.Runner.prototype.afterEach = function(afterEachFunction) { afterEachFunction.typeName = 'afterEach'; this.after_.splice(0,0,afterEachFunction); }; jasmine.Runner.prototype.finishCallback = function() { this.env.reporter.reportRunnerResults(this); }; jasmine.Runner.prototype.addSuite = function(suite) { this.suites_.push(suite); }; jasmine.Runner.prototype.add = function(block) { if (block instanceof jasmine.Suite) { this.addSuite(block); } this.queue.add(block); }; jasmine.Runner.prototype.specs = function () { var suites = this.suites(); var specs = []; for (var i = 0; i < suites.length; i++) { specs = specs.concat(suites[i].specs()); } return specs; }; jasmine.Runner.prototype.suites = function() { return this.suites_; }; jasmine.Runner.prototype.topLevelSuites = function() { var topLevelSuites = []; for (var i = 0; i < this.suites_.length; i++) { if (!this.suites_[i].parentSuite) { topLevelSuites.push(this.suites_[i]); } } return topLevelSuites; }; jasmine.Runner.prototype.results = function() { return this.queue.results(); }; /** * Internal representation of a Jasmine specification, or test. * * @constructor * @param {jasmine.Env} env * @param {jasmine.Suite} suite * @param {String} description */ jasmine.Spec = function(env, suite, description) { if (!env) { throw new Error('jasmine.Env() required'); } if (!suite) { throw new Error('jasmine.Suite() required'); } var spec = this; spec.id = env.nextSpecId ? env.nextSpecId() : null; spec.env = env; spec.suite = suite; spec.description = description; spec.queue = new jasmine.Queue(env); spec.afterCallbacks = []; spec.spies_ = []; spec.results_ = new jasmine.NestedResults(); spec.results_.description = description; spec.matchersClass = null; }; jasmine.Spec.prototype.getFullName = function() { return this.suite.getFullName() + ' ' + this.description + '.'; }; jasmine.Spec.prototype.results = function() { return this.results_; }; /** * All parameters are pretty-printed and concatenated together, then written to the spec's output. * * Be careful not to leave calls to jasmine.login production code. */ jasmine.Spec.prototype.log = function() { return this.results_.log(arguments); }; jasmine.Spec.prototype.runs = function (func) { var block = new jasmine.Block(this.env, func, this); this.addToQueue(block); return this; }; jasmine.Spec.prototype.addToQueue = function (block) { if (this.queue.isRunning()) { this.queue.insertNext(block); } else { this.queue.add(block); } }; /** * @param {jasmine.ExpectationResult} result */ jasmine.Spec.prototype.addMatcherResult = function(result) { this.results_.addResult(result); }; jasmine.Spec.prototype.expect = function(actual) { var positive = new (this.getMatchersClass_())(this.env, actual, this); positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); return positive; }; /** * Waits a fixed time period before moving to the next block. * * @deprecated Use waitsFor() instead * @param {Number} timeout milliseconds to wait */ jasmine.Spec.prototype.waits = function(timeout) { var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); this.addToQueue(waitsFunc); return this; }; /** * Waits for the latchFunction to return true before proceeding to the next block. * * @param {Function} latchFunction * @param {String} optional_timeoutMessage * @param {Number} optional_timeout */ jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { var latchFunction_ = null; var optional_timeoutMessage_ = null; var optional_timeout_ = null; for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; switch (typeof arg) { case 'function': latchFunction_ = arg; break; case 'string': optional_timeoutMessage_ = arg; break; case 'number': optional_timeout_ = arg; break; } } var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); this.addToQueue(waitsForFunc); return this; }; jasmine.Spec.prototype.fail = function (e) { var expectationResult = new jasmine.ExpectationResult({ passed: false, message: e ? jasmine.util.formatException(e) : 'Exception', trace: { stack: e.stack } }); this.results_.addResult(expectationResult); }; jasmine.Spec.prototype.getMatchersClass_ = function() { return this.matchersClass || this.env.matchersClass; }; jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { var parent = this.getMatchersClass_(); var newMatchersClass = function() { parent.apply(this, arguments); }; jasmine.util.inherit(newMatchersClass, parent); jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); this.matchersClass = newMatchersClass; }; jasmine.Spec.prototype.finishCallback = function() { this.env.reporter.reportSpecResults(this); }; jasmine.Spec.prototype.finish = function(onComplete) { this.removeAllSpies(); this.finishCallback(); if (onComplete) { onComplete(); } }; jasmine.Spec.prototype.after = function(doAfter) { if (this.queue.isRunning()) { this.queue.add(new jasmine.Block(this.env, doAfter, this), true); } else { this.afterCallbacks.unshift(doAfter); } }; jasmine.Spec.prototype.execute = function(onComplete) { var spec = this; if (!spec.env.specFilter(spec)) { spec.results_.skipped = true; spec.finish(onComplete); return; } this.env.reporter.reportSpecStarting(this); spec.env.currentSpec = spec; spec.addBeforesAndAftersToQueue(); spec.queue.start(function () { spec.finish(onComplete); }); }; jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { var runner = this.env.currentRunner(); var i; for (var suite = this.suite; suite; suite = suite.parentSuite) { for (i = 0; i < suite.before_.length; i++) { this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); } } for (i = 0; i < runner.before_.length; i++) { this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); } for (i = 0; i < this.afterCallbacks.length; i++) { this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); } for (suite = this.suite; suite; suite = suite.parentSuite) { for (i = 0; i < suite.after_.length; i++) { this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); } } for (i = 0; i < runner.after_.length; i++) { this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); } }; jasmine.Spec.prototype.explodes = function() { throw 'explodes function should not have been called'; }; jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { if (obj == jasmine.undefined) { throw "spyOn could not find an object to spy upon for " + methodName + "()"; } if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { throw methodName + '() method does not exist'; } if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { throw new Error(methodName + ' has already been spied upon'); } var spyObj = jasmine.createSpy(methodName); this.spies_.push(spyObj); spyObj.baseObj = obj; spyObj.methodName = methodName; spyObj.originalValue = obj[methodName]; obj[methodName] = spyObj; return spyObj; }; jasmine.Spec.prototype.removeAllSpies = function() { for (var i = 0; i < this.spies_.length; i++) { var spy = this.spies_[i]; spy.baseObj[spy.methodName] = spy.originalValue; } this.spies_ = []; }; /** * Internal representation of a Jasmine suite. * * @constructor * @param {jasmine.Env} env * @param {String} description * @param {Function} specDefinitions * @param {jasmine.Suite} parentSuite */ jasmine.Suite = function(env, description, specDefinitions, parentSuite) { var self = this; self.id = env.nextSuiteId ? env.nextSuiteId() : null; self.description = description; self.queue = new jasmine.Queue(env); self.parentSuite = parentSuite; self.env = env; self.before_ = []; self.after_ = []; self.children_ = []; self.suites_ = []; self.specs_ = []; }; jasmine.Suite.prototype.getFullName = function() { var fullName = this.description; for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { fullName = parentSuite.description + ' ' + fullName; } return fullName; }; jasmine.Suite.prototype.finish = function(onComplete) { this.env.reporter.reportSuiteResults(this); this.finished = true; if (typeof(onComplete) == 'function') { onComplete(); } }; jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { beforeEachFunction.typeName = 'beforeEach'; this.before_.unshift(beforeEachFunction); }; jasmine.Suite.prototype.afterEach = function(afterEachFunction) { afterEachFunction.typeName = 'afterEach'; this.after_.unshift(afterEachFunction); }; jasmine.Suite.prototype.results = function() { return this.queue.results(); }; jasmine.Suite.prototype.add = function(suiteOrSpec) { this.children_.push(suiteOrSpec); if (suiteOrSpec instanceof jasmine.Suite) { this.suites_.push(suiteOrSpec); this.env.currentRunner().addSuite(suiteOrSpec); } else { this.specs_.push(suiteOrSpec); } this.queue.add(suiteOrSpec); }; jasmine.Suite.prototype.specs = function() { return this.specs_; }; jasmine.Suite.prototype.suites = function() { return this.suites_; }; jasmine.Suite.prototype.children = function() { return this.children_; }; jasmine.Suite.prototype.execute = function(onComplete) { var self = this; this.queue.start(function () { self.finish(onComplete); }); }; jasmine.WaitsBlock = function(env, timeout, spec) { this.timeout = timeout; jasmine.Block.call(this, env, null, spec); }; jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); jasmine.WaitsBlock.prototype.execute = function (onComplete) { if (jasmine.VERBOSE) { this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); } this.env.setTimeout(function () { onComplete(); }, this.timeout); }; /** * A block which waits for some condition to become true, with timeout. * * @constructor * @extends jasmine.Block * @param {jasmine.Env} env The Jasmine environment. * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. * @param {Function} latchFunction A function which returns true when the desired condition has been met. * @param {String} message The message to display if the desired condition hasn't been met within the given time period. * @param {jasmine.Spec} spec The Jasmine spec. */ jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { this.timeout = timeout || env.defaultTimeoutInterval; this.latchFunction = latchFunction; this.message = message; this.totalTimeSpentWaitingForLatch = 0; jasmine.Block.call(this, env, null, spec); }; jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; jasmine.WaitsForBlock.prototype.execute = function(onComplete) { if (jasmine.VERBOSE) { this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); } var latchFunctionResult; try { latchFunctionResult = this.latchFunction.apply(this.spec); } catch (e) { this.spec.fail(e); onComplete(); return; } if (latchFunctionResult) { onComplete(); } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); this.spec.fail({ name: 'timeout', message: message }); this.abort = true; onComplete(); } else { this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; var self = this; this.env.setTimeout(function() { self.execute(onComplete); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); } }; jasmine.version_= { "major": 1, "minor": 3, "build": 1, "revision": 1354556913 }; ================================================ FILE: test/lib/require.js ================================================ /** vim: et:ts=4:sw=4:sts=4 * @license RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ //Not using strict: uneven strict support in browsers, #392, and causes //problems with requirejs.exec()/transpiler plugins that may not be strict. /*jslint regexp: true, nomen: true, sloppy: true */ /*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, version = '2.1.2', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, currDirRegExp = /^\.\//, op = Object.prototype, ostring = op.toString, hasOwn = op.hasOwnProperty, ap = Array.prototype, aps = ap.slice, apsp = ap.splice, isBrowser = !!(typeof window !== 'undefined' && navigator && document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined', //PS3 indicates loaded and complete, but need to wait for complete //specifically. Sequence is 'loading', 'loaded', execution, // then 'complete'. The UA check is unfortunate, but not sure how //to feature test w/o causing perf issues. readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? /^complete$/ : /^(complete|loaded)$/, defContextName = '_', //Oh the tragedy, detecting opera. See the usage of isOpera for reason. isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', contexts = {}, cfg = {}, globalDefQueue = [], useInteractive = false; function isFunction(it) { return ostring.call(it) === '[object Function]'; } function isArray(it) { return ostring.call(it) === '[object Array]'; } /** * Helper function for iterating over an array. If the func returns * a true value, it will break out of the loop. */ function each(ary, func) { if (ary) { var i; for (i = 0; i < ary.length; i += 1) { if (ary[i] && func(ary[i], i, ary)) { break; } } } } /** * Helper function for iterating over an array backwards. If the func * returns a true value, it will break out of the loop. */ function eachReverse(ary, func) { if (ary) { var i; for (i = ary.length - 1; i > -1; i -= 1) { if (ary[i] && func(ary[i], i, ary)) { break; } } } } function hasProp(obj, prop) { return hasOwn.call(obj, prop); } function getOwn(obj, prop) { return hasProp(obj, prop) && obj[prop]; } /** * Cycles over properties in an object and calls a function for each * property value. If the function returns a truthy value, then the * iteration is stopped. */ function eachProp(obj, func) { var prop; for (prop in obj) { if (hasProp(obj, prop)) { if (func(obj[prop], prop)) { break; } } } } /** * Simple function to mix in properties from source into target, * but only if target does not already have a property of the same name. */ function mixin(target, source, force, deepStringMixin) { if (source) { eachProp(source, function (value, prop) { if (force || !hasProp(target, prop)) { if (deepStringMixin && typeof value !== 'string') { if (!target[prop]) { target[prop] = {}; } mixin(target[prop], value, force, deepStringMixin); } else { target[prop] = value; } } }); } return target; } //Similar to Function.prototype.bind, but the 'this' object is specified //first, since it is easier to read/figure out what 'this' will be. function bind(obj, fn) { return function () { return fn.apply(obj, arguments); }; } function scripts() { return document.getElementsByTagName('script'); } //Allow getting a global that expressed in //dot notation, like 'a.b.c'. function getGlobal(value) { if (!value) { return value; } var g = global; each(value.split('.'), function (part) { g = g[part]; }); return g; } /** * Constructs an error with a pointer to an URL with more information. * @param {String} id the error ID that maps to an ID on a web page. * @param {String} message human readable error. * @param {Error} [err] the original error, if there is one. * * @returns {Error} */ function makeError(id, msg, err, requireModules) { var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); e.requireType = id; e.requireModules = requireModules; if (err) { e.originalError = err; } return e; } if (typeof define !== 'undefined') { //If a define is already in play via another AMD loader, //do not overwrite. return; } if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { //Do not overwrite and existing requirejs instance. return; } cfg = requirejs; requirejs = undefined; } //Allow for a require config object if (typeof require !== 'undefined' && !isFunction(require)) { //assume it is a config object. cfg = require; require = undefined; } function newContext(contextName) { var inCheckLoaded, Module, context, handlers, checkLoadedTimeoutId, config = { waitSeconds: 7, baseUrl: './', paths: {}, pkgs: {}, shim: {}, map: {}, config: {} }, registry = {}, undefEvents = {}, defQueue = [], defined = {}, urlFetched = {}, requireCounter = 1, unnormalizedCounter = 1; /** * Trims the . and .. from an array of path segments. * It will keep a leading path segment if a .. will become * the first path segment, to help with module name lookups, * which act like paths, but can be remapped. But the end result, * all paths that use this function should look normalized. * NOTE: this method MODIFIES the input array. * @param {Array} ary the array of path segments. */ function trimDots(ary) { var i, part; for (i = 0; ary[i]; i += 1) { part = ary[i]; if (part === '.') { ary.splice(i, 1); i -= 1; } else if (part === '..') { if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { //End of the line. Keep at least one non-dot //path segment at the front so it can be mapped //correctly to disk. Otherwise, there is likely //no path mapping for a path starting with '..'. //This can still fail, but catches the most reasonable //uses of .. break; } else if (i > 0) { ary.splice(i - 1, 2); i -= 2; } } } } /** * Given a relative module name, like ./something, normalize it to * a real name that can be mapped to a path. * @param {String} name the relative name * @param {String} baseName a real name that the name arg is relative * to. * @param {Boolean} applyMap apply the map config to the value. Should * only be done if this normalization is for a dependency ID. * @returns {String} normalized name */ function normalize(name, baseName, applyMap) { var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, foundMap, foundI, foundStarMap, starI, baseParts = baseName && baseName.split('/'), normalizedBaseParts = baseParts, map = config.map, starMap = map && map['*']; //Adjust any relative paths. if (name && name.charAt(0) === '.') { //If have a base name, try to normalize against it, //otherwise, assume it is a top-level require that will //be relative to baseUrl in the end. if (baseName) { if (getOwn(config.pkgs, baseName)) { //If the baseName is a package name, then just treat it as one //name to concat the name with. normalizedBaseParts = baseParts = [baseName]; } else { //Convert baseName to array, and lop off the last part, //so that . matches that 'directory' and not name of the baseName's //module. For instance, baseName of 'one/two/three', maps to //'one/two/three.js', but we want the directory, 'one/two' for //this normalization. normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); } name = normalizedBaseParts.concat(name.split('/')); trimDots(name); //Some use of packages may use a . path to reference the //'main' module name, so normalize for that. pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); name = name.join('/'); if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { name = pkgName; } } else if (name.indexOf('./') === 0) { // No baseName, so this is ID is resolved relative // to baseUrl, pull off the leading dot. name = name.substring(2); } } //Apply map config if available. if (applyMap && (baseParts || starMap) && map) { nameParts = name.split('/'); for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join('/'); if (baseParts) { //Find the longest baseName segment match in the config. //So, do joins on the biggest to smallest lengths of baseParts. for (j = baseParts.length; j > 0; j -= 1) { mapValue = getOwn(map, baseParts.slice(0, j).join('/')); //baseName segment has config, find if it has one for //this name. if (mapValue) { mapValue = getOwn(mapValue, nameSegment); if (mapValue) { //Match, update name to the new value. foundMap = mapValue; foundI = i; break; } } } } if (foundMap) { break; } //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { foundStarMap = getOwn(starMap, nameSegment); starI = i; } } if (!foundMap && foundStarMap) { foundMap = foundStarMap; foundI = starI; } if (foundMap) { nameParts.splice(0, foundI, foundMap); name = nameParts.join('/'); } } return name; } function removeScript(name) { if (isBrowser) { each(scripts(), function (scriptNode) { if (scriptNode.getAttribute('data-requiremodule') === name && scriptNode.getAttribute('data-requirecontext') === context.contextName) { scriptNode.parentNode.removeChild(scriptNode); return true; } }); } } function hasPathFallback(id) { var pathConfig = getOwn(config.paths, id); if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { removeScript(id); //Pop off the first array value, since it failed, and //retry pathConfig.shift(); context.require.undef(id); context.require([id]); return true; } } //Turns a plugin!resource to [plugin, resource] //with the plugin being undefined if the name //did not have a plugin prefix. function splitPrefix(name) { var prefix, index = name ? name.indexOf('!') : -1; if (index > -1) { prefix = name.substring(0, index); name = name.substring(index + 1, name.length); } return [prefix, name]; } /** * Creates a module mapping that includes plugin prefix, module * name, and path. If parentModuleMap is provided it will * also normalize the name via require.normalize() * * @param {String} name the module name * @param {String} [parentModuleMap] parent module map * for the module name, used to resolve relative names. * @param {Boolean} isNormalized: is the ID already normalized. * This is true if this call is done for a define() module ID. * @param {Boolean} applyMap: apply the map config to the ID. * Should only be true if this map is for a dependency. * * @returns {Object} */ function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { var url, pluginModule, suffix, nameParts, prefix = null, parentName = parentModuleMap ? parentModuleMap.name : null, originalName = name, isDefine = true, normalizedName = ''; //If no name, then it means it is a require call, generate an //internal name. if (!name) { isDefine = false; name = '_@r' + (requireCounter += 1); } nameParts = splitPrefix(name); prefix = nameParts[0]; name = nameParts[1]; if (prefix) { prefix = normalize(prefix, parentName, applyMap); pluginModule = getOwn(defined, prefix); } //Account for relative paths if there is a base name. if (name) { if (prefix) { if (pluginModule && pluginModule.normalize) { //Plugin is loaded, use its normalize method. normalizedName = pluginModule.normalize(name, function (name) { return normalize(name, parentName, applyMap); }); } else { normalizedName = normalize(name, parentName, applyMap); } } else { //A regular module. normalizedName = normalize(name, parentName, applyMap); //Normalized name may be a plugin ID due to map config //application in normalize. The map config values must //already be normalized, so do not need to redo that part. nameParts = splitPrefix(normalizedName); prefix = nameParts[0]; normalizedName = nameParts[1]; isNormalized = true; url = context.nameToUrl(normalizedName); } } //If the id is a plugin id that cannot be determined if it needs //normalization, stamp it with a unique ID so two matching relative //ids that may conflict can be separate. suffix = prefix && !pluginModule && !isNormalized ? '_unnormalized' + (unnormalizedCounter += 1) : ''; return { prefix: prefix, name: normalizedName, parentMap: parentModuleMap, unnormalized: !!suffix, url: url, originalName: originalName, isDefine: isDefine, id: (prefix ? prefix + '!' + normalizedName : normalizedName) + suffix }; } function getModule(depMap) { var id = depMap.id, mod = getOwn(registry, id); if (!mod) { mod = registry[id] = new context.Module(depMap); } return mod; } function on(depMap, name, fn) { var id = depMap.id, mod = getOwn(registry, id); if (hasProp(defined, id) && (!mod || mod.defineEmitComplete)) { if (name === 'defined') { fn(defined[id]); } } else { getModule(depMap).on(name, fn); } } function onError(err, errback) { var ids = err.requireModules, notified = false; if (errback) { errback(err); } else { each(ids, function (id) { var mod = getOwn(registry, id); if (mod) { //Set error on module, so it skips timeout checks. mod.error = err; if (mod.events.error) { notified = true; mod.emit('error', err); } } }); if (!notified) { req.onError(err); } } } /** * Internal method to transfer globalQueue items to this context's * defQueue. */ function takeGlobalQueue() { //Push all the globalDefQueue items into the context's defQueue if (globalDefQueue.length) { //Array splice in the values since the context code has a //local var ref to defQueue, so cannot just reassign the one //on context. apsp.apply(defQueue, [defQueue.length - 1, 0].concat(globalDefQueue)); globalDefQueue = []; } } handlers = { 'require': function (mod) { if (mod.require) { return mod.require; } else { return (mod.require = context.makeRequire(mod.map)); } }, 'exports': function (mod) { mod.usingExports = true; if (mod.map.isDefine) { if (mod.exports) { return mod.exports; } else { return (mod.exports = defined[mod.map.id] = {}); } } }, 'module': function (mod) { if (mod.module) { return mod.module; } else { return (mod.module = { id: mod.map.id, uri: mod.map.url, config: function () { return (config.config && getOwn(config.config, mod.map.id)) || {}; }, exports: defined[mod.map.id] }); } } }; function cleanRegistry(id) { //Clean up machinery used for waiting modules. delete registry[id]; } function breakCycle(mod, traced, processed) { var id = mod.map.id; if (mod.error) { mod.emit('error', mod.error); } else { traced[id] = true; each(mod.depMaps, function (depMap, i) { var depId = depMap.id, dep = getOwn(registry, depId); //Only force things that have not completed //being defined, so still in the registry, //and only if it has not been matched up //in the module already. if (dep && !mod.depMatched[i] && !processed[depId]) { if (getOwn(traced, depId)) { mod.defineDep(i, defined[depId]); mod.check(); //pass false? } else { breakCycle(dep, traced, processed); } } }); processed[id] = true; } } function checkLoaded() { var map, modId, err, usingPathFallback, waitInterval = config.waitSeconds * 1000, //It is possible to disable the wait interval by using waitSeconds of 0. expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), noLoads = [], reqCalls = [], stillLoading = false, needCycleCheck = true; //Do not bother if this call was a result of a cycle break. if (inCheckLoaded) { return; } inCheckLoaded = true; //Figure out the state of all the modules. eachProp(registry, function (mod) { map = mod.map; modId = map.id; //Skip things that are not enabled or in error state. if (!mod.enabled) { return; } if (!map.isDefine) { reqCalls.push(mod); } if (!mod.error) { //If the module should be executed, and it has not //been inited and time is up, remember it. if (!mod.inited && expired) { if (hasPathFallback(modId)) { usingPathFallback = true; stillLoading = true; } else { noLoads.push(modId); removeScript(modId); } } else if (!mod.inited && mod.fetched && map.isDefine) { stillLoading = true; if (!map.prefix) { //No reason to keep looking for unfinished //loading. If the only stillLoading is a //plugin resource though, keep going, //because it may be that a plugin resource //is waiting on a non-plugin cycle. return (needCycleCheck = false); } } } }); if (expired && noLoads.length) { //If wait time expired, throw error of unloaded modules. err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); err.contextName = context.contextName; return onError(err); } //Not expired, check for a cycle. if (needCycleCheck) { each(reqCalls, function (mod) { breakCycle(mod, {}, {}); }); } //If still waiting on loads, and the waiting load is something //other than a plugin resource, or there are still outstanding //scripts, then just try back later. if ((!expired || usingPathFallback) && stillLoading) { //Something is still waiting to load. Wait for it, but only //if a timeout is not already in effect. if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { checkLoadedTimeoutId = setTimeout(function () { checkLoadedTimeoutId = 0; checkLoaded(); }, 50); } } inCheckLoaded = false; } Module = function (map) { this.events = getOwn(undefEvents, map.id) || {}; this.map = map; this.shim = getOwn(config.shim, map.id); this.depExports = []; this.depMaps = []; this.depMatched = []; this.pluginMaps = {}; this.depCount = 0; /* this.exports this.factory this.depMaps = [], this.enabled, this.fetched */ }; Module.prototype = { init: function (depMaps, factory, errback, options) { options = options || {}; //Do not do more inits if already done. Can happen if there //are multiple define calls for the same module. That is not //a normal, common case, but it is also not unexpected. if (this.inited) { return; } this.factory = factory; if (errback) { //Register for errors on this module. this.on('error', errback); } else if (this.events.error) { //If no errback already, but there are error listeners //on this module, set up an errback to pass to the deps. errback = bind(this, function (err) { this.emit('error', err); }); } //Do a copy of the dependency array, so that //source inputs are not modified. For example //"shim" deps are passed in here directly, and //doing a direct modification of the depMaps array //would affect that config. this.depMaps = depMaps && depMaps.slice(0); this.errback = errback; //Indicate this module has be initialized this.inited = true; this.ignore = options.ignore; //Could have option to init this module in enabled mode, //or could have been previously marked as enabled. However, //the dependencies are not known until init is called. So //if enabled previously, now trigger dependencies as enabled. if (options.enabled || this.enabled) { //Enable this module and dependencies. //Will call this.check() this.enable(); } else { this.check(); } }, defineDep: function (i, depExports) { //Because of cycles, defined callback for a given //export can be called more than once. if (!this.depMatched[i]) { this.depMatched[i] = true; this.depCount -= 1; this.depExports[i] = depExports; } }, fetch: function () { if (this.fetched) { return; } this.fetched = true; context.startTime = (new Date()).getTime(); var map = this.map; //If the manager is for a plugin managed resource, //ask the plugin to load it now. if (this.shim) { context.makeRequire(this.map, { enableBuildCallback: true })(this.shim.deps || [], bind(this, function () { return map.prefix ? this.callPlugin() : this.load(); })); } else { //Regular dependency. return map.prefix ? this.callPlugin() : this.load(); } }, load: function () { var url = this.map.url; //Regular dependency. if (!urlFetched[url]) { urlFetched[url] = true; context.load(this.map.id, url); } }, /** * Checks is the module is ready to define itself, and if so, * define it. */ check: function () { if (!this.enabled || this.enabling) { return; } var err, cjsModule, id = this.map.id, depExports = this.depExports, exports = this.exports, factory = this.factory; if (!this.inited) { this.fetch(); } else if (this.error) { this.emit('error', this.error); } else if (!this.defining) { //The factory could trigger another require call //that would result in checking this module to //define itself again. If already in the process //of doing that, skip this work. this.defining = true; if (this.depCount < 1 && !this.defined) { if (isFunction(factory)) { //If there is an error listener, favor passing //to that instead of throwing an error. if (this.events.error) { try { exports = context.execCb(id, factory, depExports, exports); } catch (e) { err = e; } } else { exports = context.execCb(id, factory, depExports, exports); } if (this.map.isDefine) { //If setting exports via 'module' is in play, //favor that over return value and exports. After that, //favor a non-undefined return value over exports use. cjsModule = this.module; if (cjsModule && cjsModule.exports !== undefined && //Make sure it is not already the exports value cjsModule.exports !== this.exports) { exports = cjsModule.exports; } else if (exports === undefined && this.usingExports) { //exports already set the defined value. exports = this.exports; } } if (err) { err.requireMap = this.map; err.requireModules = [this.map.id]; err.requireType = 'define'; return onError((this.error = err)); } } else { //Just a literal value exports = factory; } this.exports = exports; if (this.map.isDefine && !this.ignore) { defined[id] = exports; if (req.onResourceLoad) { req.onResourceLoad(context, this.map, this.depMaps); } } //Clean up delete registry[id]; this.defined = true; } //Finished the define stage. Allow calling check again //to allow define notifications below in the case of a //cycle. this.defining = false; if (this.defined && !this.defineEmitted) { this.defineEmitted = true; this.emit('defined', this.exports); this.defineEmitComplete = true; } } }, callPlugin: function () { var map = this.map, id = map.id, //Map already normalized the prefix. pluginMap = makeModuleMap(map.prefix); //Mark this as a dependency for this plugin, so it //can be traced for cycles. this.depMaps.push(pluginMap); on(pluginMap, 'defined', bind(this, function (plugin) { var load, normalizedMap, normalizedMod, name = this.map.name, parentName = this.map.parentMap ? this.map.parentMap.name : null, localRequire = context.makeRequire(map.parentMap, { enableBuildCallback: true, skipMap: true }); //If current map is not normalized, wait for that //normalized name to load instead of continuing. if (this.map.unnormalized) { //Normalize the ID if the plugin allows it. if (plugin.normalize) { name = plugin.normalize(name, function (name) { return normalize(name, parentName, true); }) || ''; } //prefix and name should already be normalized, no need //for applying map config again either. normalizedMap = makeModuleMap(map.prefix + '!' + name, this.map.parentMap); on(normalizedMap, 'defined', bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true, ignore: true }); })); normalizedMod = getOwn(registry, normalizedMap.id); if (normalizedMod) { //Mark this as a dependency for this plugin, so it //can be traced for cycles. this.depMaps.push(normalizedMap); if (this.events.error) { normalizedMod.on('error', bind(this, function (err) { this.emit('error', err); })); } normalizedMod.enable(); } return; } load = bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true }); }); load.error = bind(this, function (err) { this.inited = true; this.error = err; err.requireModules = [id]; //Remove temp unnormalized modules for this module, //since they will never be resolved otherwise now. eachProp(registry, function (mod) { if (mod.map.id.indexOf(id + '_unnormalized') === 0) { cleanRegistry(mod.map.id); } }); onError(err); }); //Allow plugins to load other code without having to know the //context or how to 'complete' the load. load.fromText = bind(this, function (text, textAlt) { /*jslint evil: true */ var moduleName = map.name, moduleMap = makeModuleMap(moduleName), hasInteractive = useInteractive; //As of 2.1.0, support just passing the text, to reinforce //fromText only being called once per resource. Still //support old style of passing moduleName but discard //that moduleName in favor of the internal ref. if (textAlt) { text = textAlt; } //Turn off interactive script matching for IE for any define //calls in the text, then turn it back on at the end. if (hasInteractive) { useInteractive = false; } //Prime the system by creating a module instance for //it. getModule(moduleMap); //Transfer any config to this other module. if (hasProp(config.config, id)) { config.config[moduleName] = config.config[id]; } try { req.exec(text); } catch (e) { throw new Error('fromText eval for ' + moduleName + ' failed: ' + e); } if (hasInteractive) { useInteractive = true; } //Mark this as a dependency for the plugin //resource this.depMaps.push(moduleMap); //Support anonymous modules. context.completeLoad(moduleName); //Bind the value of that module to the value for this //resource ID. localRequire([moduleName], load); }); //Use parentName here since the plugin's name is not reliable, //could be some weird string with no path that actually wants to //reference the parentName's path. plugin.load(map.name, localRequire, load, config); })); context.enable(pluginMap, this); this.pluginMaps[pluginMap.id] = pluginMap; }, enable: function () { this.enabled = true; //Set flag mentioning that the module is enabling, //so that immediate calls to the defined callbacks //for dependencies do not trigger inadvertent load //with the depCount still being zero. this.enabling = true; //Enable each dependency each(this.depMaps, bind(this, function (depMap, i) { var id, mod, handler; if (typeof depMap === 'string') { //Dependency needs to be converted to a depMap //and wired up to this module. depMap = makeModuleMap(depMap, (this.map.isDefine ? this.map : this.map.parentMap), false, !this.skipMap); this.depMaps[i] = depMap; handler = getOwn(handlers, depMap.id); if (handler) { this.depExports[i] = handler(this); return; } this.depCount += 1; on(depMap, 'defined', bind(this, function (depExports) { this.defineDep(i, depExports); this.check(); })); if (this.errback) { on(depMap, 'error', this.errback); } } id = depMap.id; mod = registry[id]; //Skip special modules like 'require', 'exports', 'module' //Also, don't call enable if it is already enabled, //important in circular dependency cases. if (!hasProp(handlers, id) && mod && !mod.enabled) { context.enable(depMap, this); } })); //Enable each plugin that is used in //a dependency eachProp(this.pluginMaps, bind(this, function (pluginMap) { var mod = getOwn(registry, pluginMap.id); if (mod && !mod.enabled) { context.enable(pluginMap, this); } })); this.enabling = false; this.check(); }, on: function (name, cb) { var cbs = this.events[name]; if (!cbs) { cbs = this.events[name] = []; } cbs.push(cb); }, emit: function (name, evt) { each(this.events[name], function (cb) { cb(evt); }); if (name === 'error') { //Now that the error handler was triggered, remove //the listeners, since this broken Module instance //can stay around for a while in the registry. delete this.events[name]; } } }; function callGetModule(args) { //Skip modules already defined. if (!hasProp(defined, args[0])) { getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); } } function removeListener(node, func, name, ieName) { //Favor detachEvent because of IE9 //issue, see attachEvent/addEventListener comment elsewhere //in this file. if (node.detachEvent && !isOpera) { //Probably IE. If not it will throw an error, which will be //useful to know. if (ieName) { node.detachEvent(ieName, func); } } else { node.removeEventListener(name, func, false); } } /** * Given an event from a script node, get the requirejs info from it, * and then removes the event listeners on the node. * @param {Event} evt * @returns {Object} */ function getScriptData(evt) { //Using currentTarget instead of target for Firefox 2.0's sake. Not //all old browsers will be supported, but this one was easy enough //to support and still makes sense. var node = evt.currentTarget || evt.srcElement; //Remove the listeners once here. removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); removeListener(node, context.onScriptError, 'error'); return { node: node, id: node && node.getAttribute('data-requiremodule') }; } function intakeDefines() { var args; //Any defined modules in the global queue, intake them now. takeGlobalQueue(); //Make sure any remaining defQueue items get properly processed. while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); } else { //args are id, deps, factory. Should be normalized by the //define() function. callGetModule(args); } } } context = { config: config, contextName: contextName, registry: registry, defined: defined, urlFetched: urlFetched, defQueue: defQueue, Module: Module, makeModuleMap: makeModuleMap, nextTick: req.nextTick, /** * Set a configuration for the context. * @param {Object} cfg config object to integrate. */ configure: function (cfg) { //Make sure the baseUrl ends in a slash. if (cfg.baseUrl) { if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { cfg.baseUrl += '/'; } } //Save off the paths and packages since they require special processing, //they are additive. var pkgs = config.pkgs, shim = config.shim, objs = { paths: true, config: true, map: true }; eachProp(cfg, function (value, prop) { if (objs[prop]) { if (prop === 'map') { mixin(config[prop], value, true, true); } else { mixin(config[prop], value, true); } } else { config[prop] = value; } }); //Merge shim if (cfg.shim) { eachProp(cfg.shim, function (value, id) { //Normalize the structure if (isArray(value)) { value = { deps: value }; } if ((value.exports || value.init) && !value.exportsFn) { value.exportsFn = context.makeShimExports(value); } shim[id] = value; }); config.shim = shim; } //Adjust packages if necessary. if (cfg.packages) { each(cfg.packages, function (pkgObj) { var location; pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; location = pkgObj.location; //Create a brand new object on pkgs, since currentPackages can //be passed in again, and config.pkgs is the internal transformed //state for all package configs. pkgs[pkgObj.name] = { name: pkgObj.name, location: location || pkgObj.name, //Remove leading dot in main, so main paths are normalized, //and remove any trailing .js, since different package //envs have different conventions: some use a module name, //some use a file name. main: (pkgObj.main || 'main') .replace(currDirRegExp, '') .replace(jsSuffixRegExp, '') }; }); //Done with modifications, assing packages back to context config config.pkgs = pkgs; } //If there are any "waiting to execute" modules in the registry, //update the maps for them, since their info, like URLs to load, //may have changed. eachProp(registry, function (mod, id) { //If module already has init called, since it is too //late to modify them, and ignore unnormalized ones //since they are transient. if (!mod.inited && !mod.map.unnormalized) { mod.map = makeModuleMap(id); } }); //If a deps array or a config callback is specified, then call //require with those args. This is useful when require is defined as a //config object before require.js is loaded. if (cfg.deps || cfg.callback) { context.require(cfg.deps || [], cfg.callback); } }, makeShimExports: function (value) { function fn() { var ret; if (value.init) { ret = value.init.apply(global, arguments); } return ret || (value.exports && getGlobal(value.exports)); } return fn; }, makeRequire: function (relMap, options) { options = options || {}; function localRequire(deps, callback, errback) { var id, map, requireMod; if (options.enableBuildCallback && callback && isFunction(callback)) { callback.__requireJsBuild = true; } if (typeof deps === 'string') { if (isFunction(callback)) { //Invalid call return onError(makeError('requireargs', 'Invalid require call'), errback); } //If require|exports|module are requested, get the //value for them from the special handlers. Caveat: //this only works while module is being defined. if (relMap && hasProp(handlers, deps)) { return handlers[deps](registry[relMap.id]); } //Synchronous access to one module. If require.get is //available (as in the Node adapter), prefer that. if (req.get) { return req.get(context, deps, relMap); } //Normalize module name, if it contains . or .. map = makeModuleMap(deps, relMap, false, true); id = map.id; if (!hasProp(defined, id)) { return onError(makeError('notloaded', 'Module name "' + id + '" has not been loaded yet for context: ' + contextName + (relMap ? '' : '. Use require([])'))); } return defined[id]; } //Grab defines waiting in the global queue. intakeDefines(); //Mark all the dependencies as needing to be loaded. context.nextTick(function () { //Some defines could have been added since the //require call, collect them. intakeDefines(); requireMod = getModule(makeModuleMap(null, relMap)); //Store if map config should be applied to this require //call for dependencies. requireMod.skipMap = options.skipMap; requireMod.init(deps, callback, errback, { enabled: true }); checkLoaded(); }); return localRequire; } mixin(localRequire, { isBrowser: isBrowser, /** * Converts a module name + .extension into an URL path. * *Requires* the use of a module name. It does not support using * plain URLs like nameToUrl. */ toUrl: function (moduleNamePlusExt) { var index = moduleNamePlusExt.lastIndexOf('.'), ext = null; if (index !== -1) { ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); moduleNamePlusExt = moduleNamePlusExt.substring(0, index); } return context.nameToUrl(normalize(moduleNamePlusExt, relMap && relMap.id, true), ext); }, defined: function (id) { return hasProp(defined, makeModuleMap(id, relMap, false, true).id); }, specified: function (id) { id = makeModuleMap(id, relMap, false, true).id; return hasProp(defined, id) || hasProp(registry, id); } }); //Only allow undef on top level require calls if (!relMap) { localRequire.undef = function (id) { //Bind any waiting define() calls to this context, //fix for #408 takeGlobalQueue(); var map = makeModuleMap(id, relMap, true), mod = getOwn(registry, id); delete defined[id]; delete urlFetched[map.url]; delete undefEvents[id]; if (mod) { //Hold on to listeners in case the //module will be attempted to be reloaded //using a different config. if (mod.events.defined) { undefEvents[id] = mod.events; } cleanRegistry(id); } }; } return localRequire; }, /** * Called to enable a module if it is still in the registry * awaiting enablement. parent module is passed in for context, * used by the optimizer. */ enable: function (depMap, parent) { var mod = getOwn(registry, depMap.id); if (mod) { getModule(depMap).enable(); } }, /** * Internal method used by environment adapters to complete a load event. * A load event could be a script load or just a load pass from a synchronous * load call. * @param {String} moduleName the name of the module to potentially complete. */ completeLoad: function (moduleName) { var found, args, mod, shim = getOwn(config.shim, moduleName) || {}, shExports = shim.exports; takeGlobalQueue(); while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { args[0] = moduleName; //If already found an anonymous module and bound it //to this name, then this is some other anon module //waiting for its completeLoad to fire. if (found) { break; } found = true; } else if (args[0] === moduleName) { //Found matching define call for this script! found = true; } callGetModule(args); } //Do this after the cycle of callGetModule in case the result //of those calls/init calls changes the registry. mod = getOwn(registry, moduleName); if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { if (hasPathFallback(moduleName)) { return; } else { return onError(makeError('nodefine', 'No define call for ' + moduleName, null, [moduleName])); } } else { //A script that does not call define(), so just simulate //the call for it. callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); } } checkLoaded(); }, /** * Converts a module name to a file path. Supports cases where * moduleName may actually be just an URL. * Note that it **does not** call normalize on the moduleName, * it is assumed to have already been normalized. This is an * internal API, not a public one. Use toUrl for the public API. */ nameToUrl: function (moduleName, ext) { var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, parentPath; //If a colon is in the URL, it indicates a protocol is used and it is just //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) //or ends with .js, then assume the user meant to use an url and not a module id. //The slash is important for protocol-less URLs as well as full paths. if (req.jsExtRegExp.test(moduleName)) { //Just a plain path, not module name lookup, so just return it. //Add extension if it is included. This is a bit wonky, only non-.js things pass //an extension, this method probably needs to be reworked. url = moduleName + (ext || ''); } else { //A module that needs to be converted to a path. paths = config.paths; pkgs = config.pkgs; syms = moduleName.split('/'); //For each module name segment, see if there is a path //registered for it. Start with most specific name //and work up from it. for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); pkg = getOwn(pkgs, parentModule); parentPath = getOwn(paths, parentModule); if (parentPath) { //If an array, it means there are a few choices, //Choose the one that is desired if (isArray(parentPath)) { parentPath = parentPath[0]; } syms.splice(0, i, parentPath); break; } else if (pkg) { //If module name is just the package name, then looking //for the main module. if (moduleName === pkg.name) { pkgPath = pkg.location + '/' + pkg.main; } else { pkgPath = pkg.location; } syms.splice(0, i, pkgPath); break; } } //Join the path parts together, then figure out if baseUrl is needed. url = syms.join('/'); url += (ext || (/\?/.test(url) ? '' : '.js')); url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; } return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) : url; }, //Delegates to req.load. Broken out as a separate function to //allow overriding in the optimizer. load: function (id, url) { req.load(context, id, url); }, /** * Executes a module callack function. Broken out as a separate function * solely to allow the build system to sequence the files in the built * layer in the right sequence. * * @private */ execCb: function (name, callback, args, exports) { return callback.apply(exports, args); }, /** * callback for script loads, used to check status of loading. * * @param {Event} evt the event from the browser for the script * that was loaded. */ onScriptLoad: function (evt) { //Using currentTarget instead of target for Firefox 2.0's sake. Not //all old browsers will be supported, but this one was easy enough //to support and still makes sense. if (evt.type === 'load' || (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { //Reset interactive script so a script node is not held onto for //to long. interactiveScript = null; //Pull out the name of the module and the context. var data = getScriptData(evt); context.completeLoad(data.id); } }, /** * Callback for script errors. */ onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { return onError(makeError('scripterror', 'Script error', evt, [data.id])); } } }; context.require = context.makeRequire(); return context; } /** * Main entry point. * * If the only argument to require is a string, then the module that * is represented by that string is fetched for the appropriate context. * * If the first argument is an array, then it will be treated as an array * of dependency string names to fetch. An optional function callback can * be specified to execute when all of those dependencies are available. * * Make a local req variable to help Caja compliance (it assumes things * on a require that are not standardized), and to give a short * name for minification/local scope use. */ req = requirejs = function (deps, callback, errback, optional) { //Find the right context, use default var context, config, contextName = defContextName; // Determine if have config object in the call. if (!isArray(deps) && typeof deps !== 'string') { // deps is a config object config = deps; if (isArray(callback)) { // Adjust args if there are dependencies deps = callback; callback = errback; errback = optional; } else { deps = []; } } if (config && config.context) { contextName = config.context; } context = getOwn(contexts, contextName); if (!context) { context = contexts[contextName] = req.s.newContext(contextName); } if (config) { context.configure(config); } return context.require(deps, callback, errback); }; /** * Support require.config() to make it easier to cooperate with other * AMD loaders on globally agreed names. */ req.config = function (config) { return req(config); }; /** * Execute something after the current tick * of the event loop. Override for other envs * that have a better solution than setTimeout. * @param {Function} fn function to execute later. */ req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { setTimeout(fn, 4); } : function (fn) { fn(); }; /** * Export require as a global, but only if it does not already exist. */ if (!require) { require = req; } req.version = version; //Used to filter out dependencies that are already paths. req.jsExtRegExp = /^\/|:|\?|\.js$/; req.isBrowser = isBrowser; s = req.s = { contexts: contexts, newContext: newContext }; //Create default context. req({}); //Exports some context-sensitive methods on global require. each([ 'toUrl', 'undef', 'defined', 'specified' ], function (prop) { //Reference from contexts instead of early binding to default context, //so that during builds, the latest instance of the default context //with its config gets used. req[prop] = function () { var ctx = contexts[defContextName]; return ctx.require[prop].apply(ctx, arguments); }; }); if (isBrowser) { head = s.head = document.getElementsByTagName('head')[0]; //If BASE tag is in play, using appendChild is a problem for IE6. //When that browser dies, this can be removed. Details in this jQuery bug: //http://dev.jquery.com/ticket/2709 baseElement = document.getElementsByTagName('base')[0]; if (baseElement) { head = s.head = baseElement.parentNode; } } /** * Any errors that require explicitly generates will be passed to this * function. Intercept/override it if you want custom error handling. * @param {Error} err the error object. */ req.onError = function (err) { throw err; }; /** * Does the request to load a module for the browser case. * Make this a separate function to allow other environments * to override it. * * @param {Object} context the require context to find state. * @param {String} moduleName the name of the module. * @param {Object} url the URL to the module. */ req.load = function (context, moduleName, url) { var config = (context && context.config) || {}, node; if (isBrowser) { //In the browser so use a script tag node = config.xhtml ? document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : document.createElement('script'); node.type = config.scriptType || 'text/javascript'; node.charset = 'utf-8'; node.async = true; node.setAttribute('data-requirecontext', context.contextName); node.setAttribute('data-requiremodule', moduleName); //Set up load listener. Test attachEvent first because IE9 has //a subtle issue in its addEventListener and script onload firings //that do not match the behavior of all other browsers with //addEventListener support, which fire the onload event for a //script right after the script execution. See: //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution //UNFORTUNATELY Opera implements attachEvent but does not follow the script //script execution mode. if (node.attachEvent && //Check if node.attachEvent is artificially added by custom script or //natively supported by browser //read https://github.com/jrburke/requirejs/issues/187 //if we can NOT find [native code] then it must NOT natively supported. //in IE8, node.attachEvent does not have toString() //Note the test for "[native code" with no closing brace, see: //https://github.com/jrburke/requirejs/issues/273 !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera) { //Probably IE. IE (at least 6-8) do not fire //script onload right after executing the script, so //we cannot tie the anonymous define call to a name. //However, IE reports the script as being in 'interactive' //readyState at the time of the define call. useInteractive = true; node.attachEvent('onreadystatechange', context.onScriptLoad); //It would be great to add an error handler here to catch //404s in IE9+. However, onreadystatechange will fire before //the error handler, so that does not help. If addEvenListener //is used, then IE will fire error before load, but we cannot //use that pathway given the connect.microsoft.com issue //mentioned above about not doing the 'script execute, //then fire the script load event listener before execute //next script' that other browsers do. //Best hope: IE10 fixes the issues, //and then destroys all installs of IE 6-9. //node.attachEvent('onerror', context.onScriptError); } else { node.addEventListener('load', context.onScriptLoad, false); node.addEventListener('error', context.onScriptError, false); } node.src = url; //For some cache cases in IE 6-8, the script executes before the end //of the appendChild execution, so to tie an anonymous define //call to the module name (which is stored on the node), hold on //to a reference to this node, but clear after the DOM insertion. currentlyAddingScript = node; if (baseElement) { head.insertBefore(node, baseElement); } else { head.appendChild(node); } currentlyAddingScript = null; return node; } else if (isWebWorker) { //In a web worker, use importScripts. This is not a very //efficient use of importScripts, importScripts will block until //its script is downloaded and evaluated. However, if web workers //are in play, the expectation that a build has been done so that //only one script needs to be loaded anyway. This may need to be //reevaluated if other use cases become common. importScripts(url); //Account for anonymous modules context.completeLoad(moduleName); } }; function getInteractiveScript() { if (interactiveScript && interactiveScript.readyState === 'interactive') { return interactiveScript; } eachReverse(scripts(), function (script) { if (script.readyState === 'interactive') { return (interactiveScript = script); } }); return interactiveScript; } //Look for a data-main script attribute, which could also adjust the baseUrl. if (isBrowser) { //Figure out baseUrl. Get it from the script tag with require.js in it. eachReverse(scripts(), function (script) { //Set the 'head' where we can append children by //using the script's parent. if (!head) { head = script.parentNode; } //Look for a data-main attribute to set main script for the page //to load. If it is there, the path to data main becomes the //baseUrl, if it is not already set. dataMain = script.getAttribute('data-main'); if (dataMain) { //Set final baseUrl if there is not already an explicit one. if (!cfg.baseUrl) { //Pull off the directory of data-main for use as the //baseUrl. src = dataMain.split('/'); mainScript = src.pop(); subPath = src.length ? src.join('/') + '/' : './'; cfg.baseUrl = subPath; dataMain = mainScript; } //Strip off any trailing .js since dataMain is now //like a module name. dataMain = dataMain.replace(jsSuffixRegExp, ''); //Put the data-main script in the files to load. cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; return true; } }); } /** * The function that handles definitions of modules. Differs from * require() in that a string for the module should be the first argument, * and the function to execute after dependencies are loaded should * return a value to define the module corresponding to the first argument's * name. */ define = function (name, deps, callback) { var node, context; //Allow for anonymous modules if (typeof name !== 'string') { //Adjust args appropriately callback = deps; deps = name; name = null; } //This module may not have dependencies if (!isArray(deps)) { callback = deps; deps = []; } //If no name, and callback is a function, then figure out if it a //CommonJS thing with dependencies. if (!deps.length && isFunction(callback)) { //Remove comments from the callback string, //look for require calls, and pull them into the dependencies, //but only if there are function args. if (callback.length) { callback .toString() .replace(commentRegExp, '') .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); //May be a CommonJS thing even without require calls, but still //could use exports, and module. Avoid doing exports and module //work though if it just needs require. //REQUIRES the function to expect the CommonJS variables in the //order listed below. deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); } } //If in IE 6-8 and hit an anonymous define() call, do the interactive //work. if (useInteractive) { node = currentlyAddingScript || getInteractiveScript(); if (node) { if (!name) { name = node.getAttribute('data-requiremodule'); } context = contexts[node.getAttribute('data-requirecontext')]; } } //Always save off evaluating the def call until the script onload handler. //This allows multiple modules to be in a file without prematurely //tracing dependencies, and allows for anonymous module support, //where the module name is not known until the script onload event //occurs. If no context, use the global queue, and get it processed //in the onscript load callback. (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); }; define.amd = { jQuery: true }; /** * Executes the text. Normally just uses eval, but can be modified * to use a better, environment-specific call. Only used for transpiling * loader plugins, not for plain JS modules. * @param {String} text the text to execute/evaluate. */ req.exec = function (text) { /*jslint evil: true */ return eval(text); }; //Set up with config info. req(cfg); }(this)); ================================================ FILE: test/lib/testloader.js ================================================ function testLoader(){ var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function(spec) { return htmlReporter.specFilter(spec); }; var currentWindowOnload = window.onload; window.onload = function() { var count = 0; var loadCoffee = function(files) { for (var i = 0, len = files.length; i < len; i++) { count++; CoffeeScript.load(files[i], function() { count--; if (!count) { jasmine.getFixtures().fixturesPath = 'fixtures'; execJasmine(); } }); } }; if (currentWindowOnload) { currentWindowOnload(); } loadCoffee([ 'waypoints.coffee', 'infinite.coffee', 'sticky.coffee' ]); }; function execJasmine() { jasmineEnv.execute(); } if (document.readyState === 'complete'){ window.onload(); } } ================================================ FILE: test/settings.js ================================================ /* global jasmine, Waypoint */ 'use strict' jasmine.getFixtures().fixturesPath = 'test/fixtures' jasmine.getEnv().defaultTimeoutInterval = 1000 Waypoint.requestAnimationFrame = function(callback) { callback() } ================================================ FILE: test/sticky-spec.js ================================================ 'use strict' /* global * describe, it, beforeEach, afterEach, expect, spyOn, waits, runs, * waitsFor, loadFixtures, Waypoint, jasmine */ describe('Waypoint Sticky Shortcut', function() { var $ = window.jQuery var $scroller = $(window) var $sticky, waypoint, handlerSpy beforeEach(function() { loadFixtures('sticky.html') $sticky = $('.sticky') }) describe('with default options', function() { beforeEach(function() { handlerSpy = jasmine.createSpy('on handler') waypoint = new Waypoint.Sticky({ element: $sticky[0], handler: handlerSpy }) }) afterEach(function() { if (waypoint) { waypoint.destroy() } $scroller.scrollTop(0) }) describe('on init', function() { afterEach(function() { waypoint.destroy() }) it('returns an instance of the Waypoint.Sticky class', function() { expect(waypoint instanceof Waypoint.Sticky).toBeTruthy() }) it('wraps the sticky element on init', function() { expect($sticky.parent()).toHaveClass('sticky-wrapper') }) describe('when sticky element is scrolled to', function() { beforeEach(function() { runs(function() { $scroller.scrollTop($sticky.offset().top) }) waitsFor(function() { return $sticky.hasClass('stuck') }, 'stuck class to apply') }) it('adds/removes stuck class', function() { runs(function() { $scroller.scrollTop($scroller.scrollTop() - 1) }) waitsFor(function() { return !$sticky.hasClass('stuck') }) }) it('gives the wrapper the same height as the sticky element', function() { expect($sticky.parent().height()).toEqual($sticky.outerHeight()) }) it('executes handler option after stuck class applied', function() { expect(handlerSpy).toHaveBeenCalled() }) }) }) describe('#destroy', function() { beforeEach(function() { runs(function() { $scroller.scrollTop($sticky.offset().top) }) waitsFor(function() { return handlerSpy.callCount }) runs(function() { waypoint.destroy() }) }) it('unwraps the sticky element', function() { expect($sticky.parent()).not.toHaveClass('sticky-wrapper') }) it('removes the stuck class', function() { expect($sticky).not.toHaveClass('stuck') }) }) }) describe('with wrapper false', function() { beforeEach(function() { waypoint = new Waypoint.Sticky({ element: $sticky[0], handler: handlerSpy, wrapper: false }) }) it('does not wrap the sticky element', function() { expect($sticky.parent()).not.toHaveClass('sticky-wrapper') }) it('does not unwrap on destroy', function() { var parent = waypoint.wrapper waypoint.destroy() expect(parent).toBe(waypoint.wrapper) }) }) }) ================================================ FILE: test/waypoint-spec.js ================================================ 'use strict' /* global * describe, it, beforeEach, afterEach, expect, spyOn, waits, runs, * waitsFor, loadFixtures, Waypoint */ window.jQuery.each(Waypoint.adapters, function(i, adapter) { describe(adapter.name + ' adapter: ', function() { describe('Waypoint', function() { var $ = window.jQuery var standard = 50 var hit, $scroller, waypoint, $target, returnValue function setHitTrue() { hit = true } function hitToBeTrue() { return hit } beforeEach(function() { Waypoint.Adapter = adapter.Adapter loadFixtures('standard.html') $scroller = $(window) hit = false waypoint = null }) afterEach(function() { Waypoint.destroyAll() $scroller.scrollTop(0).scrollLeft(0) }) describe('new Waypoint()', function() { it('errors out', function() { expect(function() { waypoint = new Waypoint() }).toThrow() }) }) describe('new Waypoint(options)', function() { it('returns an instance of the Waypoint class', function() { waypoint = new Waypoint({ element: document.getElementById('same1'), handler: function() {} }) expect(waypoint instanceof Waypoint).toBeTruthy() }) it('requires the element option', function() { expect(function() { waypoint = new Waypoint({ handler: function() {} }) }).toThrow() }) it('requires the handler option', function() { expect(function() { waypoint = new Waypoint({ element: document.getElementById('same1') }) }).toThrow() }) it('triggers down on new already-reached waypoints', function() { runs(function() { $target = $('#same2') $scroller.scrollTop($target.offset().top + 1) waypoint = new Waypoint({ element: $target[0], handler: function(direction) { hit = direction === 'down' } }) }) waitsFor(hitToBeTrue, 'callback to trigger') }) }) describe('handler option', function() { var currentDirection beforeEach(function() { $target = $('#same1') currentDirection = null waypoint = new Waypoint({ element: $target[0], handler: function(direction) { currentDirection = direction } }) }) it('triggers with direction parameter', function() { runs(function() { $scroller.scrollTop($target.offset().top) }) waitsFor(function() { return currentDirection === 'down' }, 'down to trigger') runs(function() { $scroller.scrollTop($target.offset().top - 1) }) waitsFor(function() { return currentDirection === 'up' }, 'up to trigger') }) }) describe('offset option', function() { beforeEach(function() { $target = $('#same1') }) it('takes a px offset', function(){ runs(function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, offset: 50 }) $scroller.scrollTop($target.offset().top - 51) expect(hit).toBeFalsy() $scroller.scrollTop($target.offset().top - 50) }) waitsFor(hitToBeTrue, 'callback to trigger') }) it('takes a % offset', function() { var trigger = $target.offset().top - Waypoint.viewportHeight() * 0.37 runs(function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, offset: '37%' }) $scroller.scrollTop(trigger - 1) expect(hit).toBeFalsy() $scroller.scrollTop(trigger) }) waitsFor(hitToBeTrue, 'callback to trigger') }) it('takes a function offset', function() { runs(function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, offset: function() { return -$(this.element).height() } }) $scroller.scrollTop($target.offset().top + $target.height() - 1) expect(hit).toBeFalsy() $scroller.scrollTop($target.offset().top + $target.height()) }) waitsFor(hitToBeTrue, 'callback to trigger') }) it('takes a bottom-in-view function alias', function() { var top = $target.offset().top var height = $target.outerHeight() var windowHeight = Waypoint.viewportHeight() var inview = top + height - windowHeight runs(function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, offset: 'bottom-in-view' }) $scroller.scrollTop(inview - 1) expect(hit).toBeFalsy() $scroller.scrollTop(inview) }) waitsFor(hitToBeTrue, 'callback to trigger') }) }) describe('context option', function() { beforeEach(function() { $scroller = $('#bottom') $target = $('#inner3') }) it('works with px offset', function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, context: $scroller[0], offset: 10 }) runs(function() { $scroller.scrollTop(189) expect(hit).toBeFalsy() $scroller.scrollTop(190) }) waitsFor(hitToBeTrue, 'callback to trigger') }) it('works with % offset', function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, context: $scroller[0], offset: '100%' }) runs(function() { $scroller.scrollTop(149) expect(hit).toBeFalsy() $scroller.scrollTop(150) }) waitsFor(hitToBeTrue, 'callback to trigger') }) it('works with function offset', function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, context: $scroller[0], offset: function() { return $(this.element).height() / 2 } }) runs(function() { $scroller.scrollTop(149) expect(hit).toBeFalsy() $scroller.scrollTop(150) }) waitsFor(hitToBeTrue, 'callback to trigger') }) it('works with bottom-in-view offset alias', function() { waypoint = new Waypoint({ element: $target[0], handler: setHitTrue, context: $scroller[0], offset: 'bottom-in-view' }) runs(function() { $scroller.scrollTop(249) expect(hit).toBeFalsy() $scroller.scrollTop(250) }) waitsFor(hitToBeTrue, 'callback to trigger') }) }) describe('horizontal option', function() { var currentDirection beforeEach(function() { currentDirection = null $target = $('#same1') waypoint = new Waypoint({ element: $target[0], horizontal: true, handler: function(direction) { currentDirection = direction } }) }) it('triggers right/left directions', function() { runs(function() { $scroller.scrollLeft($target.offset().left) }) waitsFor(function() { return currentDirection === 'right' }, 'right direction to trigger') runs(function() { $scroller.scrollLeft($target.offset().left - 1) }) waitsFor(function() { return currentDirection === 'left' }) }) }) describe('continuous option', function() { var $later, laterWaypoint, hitCount, hitWaypoint function incrementHitCount() { hitCount += 1 hitWaypoint = this } beforeEach(function() { $target = $('#same1') $later = $('#near1') hitCount = 0 waypoint = new Waypoint({ element: $target[0], continuous: false, handler: incrementHitCount }) laterWaypoint = new Waypoint({ element: $later[0], continuous: false, handler: incrementHitCount }) }) it('does not trigger the earlier waypoint', function() { runs(function() { $scroller.scrollTop($later.offset().top) }) waitsFor(function() { return hitCount }, 'later callback to trigger') runs(function() { expect(hitCount).toEqual(1) expect(hitWaypoint).toEqual(laterWaypoint) }) }) it('prevents earlier trigger on refresh', function() { runs(function() { $target.css('top', '-1px') $later.css('top', '-2px') Waypoint.refreshAll() }) waitsFor(function() { return hitCount }, 'later callback to trigger') runs(function() { expect(hitCount).toEqual(1) expect(hitWaypoint).toEqual(waypoint) }) }) }) describe('with window as the waypoint element', function() { beforeEach(function() { $target = $(window) waypoint = new Waypoint({ element: $target[0], offset: -$target.height(), handler: setHitTrue }) }) it('triggers waypoint', function() { runs(function() { $target.scrollTop($target.height() + 1) }) waitsFor(hitToBeTrue, 'callback to trigger') }) }) describe('#disable()', function() { beforeEach(function() { $target = $('#same1') waypoint = new Waypoint({ element: $target[0], handler: setHitTrue }) returnValue = waypoint.disable() }) it('returns the same waypoint object for chaining', function() { expect(returnValue).toEqual(waypoint) }) it('disables callback triggers', function() { runs(function() { $scroller.scrollTop($target.offset().top) }) waits(standard) runs(function() { expect(hit).toBeFalsy() }) }) }) describe('#enable()', function() { beforeEach(function() { $target = $('#same1') waypoint = new Waypoint({ element: $target[0], handler: setHitTrue }) waypoint.disable() returnValue = waypoint.enable() }) it('returns the same waypoint instance for chaining', function() { expect(returnValue).toEqual(waypoint) }) it('enables callback triggers', function() { runs(function() { $scroller.scrollTop($target.offset().top) }) waitsFor(hitToBeTrue, 'callback to trigger') }) }) describe('#destroy()', function() { beforeEach(function() { $target = $('#same1') waypoint = new Waypoint({ element: $target[0], handler: setHitTrue }) returnValue = waypoint.destroy() }) it('returns undefined', function() { expect(returnValue).toBeUndefined() }) it('no longer triggers callbacks', function() { runs(function() { $scroller.scrollTop($target.offset().top) }) waits(standard) runs(function() { expect(hit).toBeFalsy() }) }) }) describe('#previous', function() { beforeEach(function() { $target = $('#same1') waypoint = new Waypoint({ element: $target[0], handler: setHitTrue }) }) it('calls previous on the waypoint group', function() { spyOn(waypoint.group, 'previous') waypoint.previous() expect(waypoint.group.previous).toHaveBeenCalledWith(waypoint) }) it('returns the group call results', function() { expect(waypoint.previous()).toBeNull() }) }) describe('#next', function() { beforeEach(function() { $target = $('#same1') waypoint = new Waypoint({ element: $target[0], handler: setHitTrue }) }) it('calls next on the waypoint group', function() { spyOn(waypoint.group, 'next') waypoint.next() expect(waypoint.group.next).toHaveBeenCalledWith(waypoint) }) it('returns the group call results', function() { expect(waypoint.next()).toBeNull() }) }) describe('Waypoint.viewportHeight()', function() { it('returns window innerHeight if it exists', function() { var height = Waypoint.viewportHeight() if (window.innerHeight) { expect(height).toEqual(window.innerHeight) } else { expect(height).toEqual(document.documentElement.clientHeight) } }) }) describe('Waypoint.viewportWidth()', function() { it('returns client width', function() { var clientWidth = document.documentElement.clientWidth expect(Waypoint.viewportWidth()).toEqual(clientWidth) }) }) describe('Waypoint.refreshAll()', function() { it('is an alias for Waypoint.Context.refreshAll', function() { spyOn(Waypoint.Context, 'refreshAll') Waypoint.refreshAll() expect(Waypoint.Context.refreshAll).toHaveBeenCalled() }) }) describe('Waypoint.destroyAll()', function() { it('calls destroy on all waypoints', function() { var secondWaypoint = new Waypoint({ element: $('#same1')[0], handler: function() {} }) waypoint = new Waypoint({ element: $('#same1')[0], handler: function() {} }) spyOn(secondWaypoint, 'destroy').andCallThrough() spyOn(waypoint, 'destroy').andCallThrough() Waypoint.destroyAll() expect(secondWaypoint.destroy).toHaveBeenCalled() expect(waypoint.destroy).toHaveBeenCalled() }) }) describe('Waypoint.disableAll()', function() { it('calls disable on all waypoints', function() { var secondWaypoint = new Waypoint({ element: $('#same1')[0], handler: function() {} }) waypoint = new Waypoint({ element: $('#same1')[0], handler: function() {} }) spyOn(secondWaypoint, 'disable').andCallThrough() spyOn(waypoint, 'disable').andCallThrough() Waypoint.disableAll() expect(secondWaypoint.disable).toHaveBeenCalled() expect(waypoint.disable).toHaveBeenCalled() }) }) describe('Waypoint.enableAll()', function() { var secondWaypoint beforeEach(function() { secondWaypoint = new Waypoint({ element: $('#same1')[0], handler: function() {} }) waypoint = new Waypoint({ element: $('#same1')[0], handler: function() {} }) Waypoint.disableAll() spyOn(Waypoint.Context, 'refreshAll').andCallThrough() Waypoint.enableAll() }) it('sets enabled on all waypoints', function() { expect(secondWaypoint.enabled).toBeTruthy() expect(waypoint.enabled).toBeTruthy() }) it('refreshes all contexts', function() { expect(Waypoint.Context.refreshAll).toHaveBeenCalled() }) }) }) }) }) ================================================ FILE: testem.json ================================================ { "framework": "jasmine", "launch_in_dev": [ "PhantomJS" ], "launch_in_ci": [ "PhantomJS" ], "src_files": [ "bower_components/jquery/dist/jquery.js", "bower_components/jasmine-jquery/lib/jasmine-jquery.js", "bower_components/zepto/zepto.js", "src/waypoint.js", "src/context.js", "src/group.js", "src/debug.js", "src/adapters/*.js", "src/shortcuts/*.js", "test/settings.js", "test/*-spec.js" ] }